Compare commits
500 Commits
b6140063c7
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20986af1ba | ||
|
|
34d46a85f1 | ||
|
|
f8f66f0e33 | ||
|
|
58f0427350 | ||
|
|
e3db26323b | ||
|
|
4788434445 | ||
|
|
07bc4a6654 | ||
|
|
bd49edb586 | ||
|
|
f4a9cc9cda | ||
|
|
8817663371 | ||
|
|
19b8b92f49 | ||
|
|
f584b49cd4 | ||
|
|
13eefdd50c | ||
|
|
3cd9031eef | ||
|
|
94c901a233 | ||
|
|
1819c31267 | ||
|
|
00371b1dda | ||
|
|
2d48d2f536 | ||
|
|
4214ed9e10 | ||
|
|
198493cac1 | ||
|
|
f57347b834 | ||
|
|
f9dff68339 | ||
|
|
48bd884690 | ||
|
|
b85b6833b6 | ||
|
|
29ea0c5b84 | ||
|
|
508218a1c5 | ||
|
|
98774036cd | ||
|
|
e15e1aa4c8 | ||
|
|
1c6c0eaf84 | ||
|
|
02b0708426 | ||
|
|
d433ceb4a9 | ||
|
|
d76abcf512 | ||
|
|
6ca9946499 | ||
|
|
a549773eb2 | ||
|
|
faf594b2f6 | ||
|
|
185f5480fa | ||
|
|
b4a60bcbe2 | ||
|
|
d57b023769 | ||
|
|
4b5f0bcdd6 | ||
|
|
3f9ce06ed6 | ||
|
|
3def4d7449 | ||
|
|
4b9d78d0d5 | ||
|
|
1f6f8a768f | ||
|
|
a7c61d9306 | ||
|
|
0247eaeda9 | ||
|
|
f9dfa466f0 | ||
|
|
c2f99f5c62 | ||
|
|
6f6dd3bfac | ||
|
|
e7f0af8e6e | ||
|
|
3bd0d79fdc | ||
|
|
1e213ddbc4 | ||
|
|
839fb4c4b7 | ||
|
|
35afcf03bb | ||
|
|
5864108f99 | ||
|
|
d486c9df79 | ||
|
|
31e627ce20 | ||
|
|
ca565e3c3e | ||
|
|
12861b80a1 | ||
|
|
02b1d28a6b | ||
|
|
f3850a6e2f | ||
|
|
a9b4a71c0b | ||
|
|
8df803b3d8 | ||
|
|
2db2bc4c66 | ||
|
|
ae837b71aa | ||
|
|
fd3684c436 | ||
|
|
7fcf4ce50b | ||
|
|
4199335ef8 | ||
|
|
37281232c0 | ||
|
|
8b3022bed0 | ||
|
|
8acf72c7bf | ||
|
|
f097a491ae | ||
|
|
e90e1911e8 | ||
|
|
d6f1cae7b7 | ||
|
|
0754b4128f | ||
|
|
1cb8ac3fff | ||
|
|
f13623f4fc | ||
|
|
2cf9a20910 | ||
|
|
59191cc42e | ||
|
|
98132eb67c | ||
|
|
204748699e | ||
|
|
7d90d4b0f0 | ||
|
|
744f02677d | ||
|
|
8fd9f21c78 | ||
|
|
5dfcf7a621 | ||
|
|
65602f09f2 | ||
|
|
02d6ee4102 | ||
|
|
c9fffbeb12 | ||
|
|
ce692bb763 | ||
|
|
1e7e175389 | ||
|
|
c9d9dd8144 | ||
|
|
03f8facde0 | ||
|
|
da4325475c | ||
|
|
4cb7522251 | ||
|
|
578b3507fd | ||
|
|
b976fed8e7 | ||
|
|
48382c4592 | ||
|
|
095b454539 | ||
|
|
cbdccfb5a7 | ||
|
|
32719b45ea | ||
|
|
b32f60d94d | ||
|
|
d524304b29 | ||
|
|
7689ec590a | ||
|
|
6517b04215 | ||
|
|
51fbdc0f71 | ||
|
|
4bf55749bb | ||
|
|
9e8c9bc508 | ||
|
|
e1039703d1 | ||
|
|
ace3f8c4f9 | ||
|
|
30c09d0613 | ||
|
|
dec9068cc8 | ||
|
|
19cfd84e7d | ||
|
|
d00dfab898 | ||
|
|
be27894f95 | ||
|
|
9b0d91a615 | ||
|
|
ad2fd93e02 | ||
|
|
5b425260c9 | ||
|
|
31133f5704 | ||
|
|
93dad0b896 | ||
|
|
8969fb7cb6 | ||
|
|
82c7c3772e | ||
|
|
df217b167e | ||
|
|
2f85aef136 | ||
|
|
b4d63a709b | ||
|
|
f64349d802 | ||
|
|
86ace28066 | ||
|
|
b46449a6e4 | ||
|
|
19b2fce904 | ||
|
|
a7df2c689a | ||
|
|
6ce8acdb2e | ||
|
|
149f003175 | ||
|
|
f0ca0fbbd4 | ||
|
|
3e5fc722e6 | ||
|
|
a646d7908a | ||
|
|
cfe8df43d1 | ||
|
|
743ccd1172 | ||
|
|
3c5bd187b7 | ||
|
|
27035eb2ca | ||
|
|
7a308e2261 | ||
|
|
3cc60986ab | ||
|
|
13edd23245 | ||
|
|
bc386908f7 | ||
|
|
c1074d8a2c | ||
|
|
6ee209f9f6 | ||
|
|
230cc08182 | ||
|
|
d60a11995e | ||
|
|
68886573a8 | ||
|
|
fabd325976 | ||
|
|
3a56415968 | ||
|
|
ed5e962370 | ||
|
|
dd39c3e63c | ||
|
|
02485de52c | ||
|
|
8891cd23af | ||
|
|
661cebdb24 | ||
|
|
8b3b32422f | ||
|
|
dffa773acc | ||
|
|
51c6d1f0a6 | ||
|
|
bd35f5c3eb | ||
|
|
7409427ce5 | ||
|
|
7bc843d440 | ||
|
|
6c1dd703bc | ||
|
|
046831b4e5 | ||
|
|
937af27698 | ||
|
|
5469ff6826 | ||
|
|
f8e6fcac46 | ||
|
|
ab96ef889d | ||
|
|
b1e618e07c | ||
|
|
62ee2ef01f | ||
|
|
04125a1495 | ||
|
|
110a90ed7a | ||
|
|
bfc14777be | ||
|
|
4e34e70a11 | ||
|
|
2d2bc7be83 | ||
|
|
5d6c4d6660 | ||
|
|
ab8895b008 | ||
|
|
d5e349ee88 | ||
|
|
760e5a118a | ||
|
|
2d78e39ca1 | ||
|
|
afd9193dea | ||
|
|
bc7b932c5c | ||
|
|
4807038619 | ||
|
|
e18024b851 | ||
|
|
1dfe702c58 | ||
|
|
04a63ced87 | ||
|
|
50e3e21634 | ||
|
|
5e5436b02c | ||
|
|
72016c5da1 | ||
|
|
bef6e88bf7 | ||
|
|
3789e476f7 | ||
|
|
ba71d66db8 | ||
|
|
af55143292 | ||
|
|
b824186c37 | ||
|
|
5034eb1da5 | ||
|
|
5e44a273fc | ||
|
|
484381c6e5 | ||
|
|
349e0933c3 | ||
|
|
08556c9d40 | ||
|
|
687bc7a9aa | ||
|
|
5a34054024 | ||
|
|
306bfa2b82 | ||
|
|
506790aefa | ||
|
|
ab1ef2c30b | ||
|
|
61bc1a265c | ||
|
|
9c45f3e13e | ||
|
|
23ad29fb2d | ||
|
|
5b64c6adcf | ||
|
|
dd42f5e54e | ||
|
|
2d7b7818d7 | ||
|
|
c27eb37852 | ||
|
|
bc48cf801b | ||
|
|
e46661ba15 | ||
|
|
9cb71af85b | ||
|
|
241ff714b8 | ||
|
|
db43de19c4 | ||
|
|
38c28c3fb6 | ||
|
|
0df1149618 | ||
|
|
aeafcb5b97 | ||
|
|
324962b0fc | ||
|
|
f5f3774daf | ||
|
|
e666dc573a | ||
|
|
11362a5689 | ||
|
|
7c7e641d1f | ||
|
|
fabdd192dd | ||
|
|
8d7ddd46be | ||
|
|
4b91bc9dbb | ||
|
|
80c6f0b7a7 | ||
|
|
4eff829a30 | ||
|
|
96ca578c70 | ||
|
|
7a0110180d | ||
|
|
b36fe7a67e | ||
|
|
6e73662860 | ||
|
|
318f75a7cc | ||
|
|
bc5ed9e602 | ||
|
|
8c8d17a1c7 | ||
|
|
71dee043a3 | ||
|
|
059078ea8f | ||
|
|
674fe000f4 | ||
|
|
85477fe46e | ||
|
|
dced175d7a | ||
|
|
bd857b840b | ||
|
|
5d1c395340 | ||
|
|
0e17b37156 | ||
|
|
fb48c44655 | ||
|
|
7378024235 | ||
|
|
1c985f28a2 | ||
|
|
449c0a8806 | ||
|
|
e1e42ea188 | ||
|
|
823eef76b0 | ||
|
|
3b0b5ff032 | ||
|
|
6112b4b207 | ||
|
|
9e8e967eb9 | ||
|
|
697082193f | ||
|
|
86d68fd5e5 | ||
|
|
ffa8ac73de | ||
|
|
f01f3b02f4 | ||
|
|
ad4e873d2f | ||
|
|
a77e22a3ea | ||
|
|
1fa91279e2 | ||
|
|
debdb93935 | ||
|
|
81cdb4afd9 | ||
|
|
bc08cd3c8c | ||
|
|
c24078b29d | ||
|
|
f04748aa5c | ||
|
|
5ce97283f1 | ||
|
|
d6f794a094 | ||
|
|
47bbf12176 | ||
|
|
2cee988ada | ||
|
|
04989762d9 | ||
|
|
89db6591a0 | ||
|
|
d173fb7842 | ||
|
|
4133c13cf8 | ||
|
|
a1eddf813d | ||
|
|
39b4a6d8a6 | ||
|
|
e4cf9d6a68 | ||
|
|
f29538762b | ||
|
|
7616a49ff8 | ||
|
|
42aefdd2f1 | ||
|
|
5fadb76a20 | ||
|
|
5474eac554 | ||
|
|
a12a8830d4 | ||
|
|
6c5f3aac85 | ||
|
|
6e164cbdfb | ||
|
|
af694f6f6c | ||
|
|
c5ce13b13c | ||
|
|
0026cae639 | ||
|
|
376177d78e | ||
|
|
6c9ee005fd | ||
|
|
82c5aeaaa0 | ||
|
|
14c279cc80 | ||
|
|
67c6f11892 | ||
|
|
edf35b7dd0 | ||
|
|
de886dcfcc | ||
|
|
67f019713a | ||
|
|
2af396a2b8 | ||
|
|
20f12c97c1 | ||
|
|
15c4bcd48e | ||
|
|
e429bbbcdb | ||
|
|
020fd63c97 | ||
|
|
2771503b6f | ||
|
|
65458cf491 | ||
|
|
c23fdbf310 | ||
|
|
dfeed305e1 | ||
|
|
bc5485d622 | ||
|
|
d3b2949ff7 | ||
|
|
f376de2b48 | ||
|
|
459fca064c | ||
|
|
f436f84696 | ||
|
|
73e795e29f | ||
|
|
a709ac7ee0 | ||
|
|
beab35a25e | ||
|
|
aa0d0e86a5 | ||
|
|
f8a043e59f | ||
|
|
441fb5b5be | ||
|
|
29ea6f9142 | ||
|
|
cd22f62d60 | ||
|
|
b3ffdf8469 | ||
|
|
6c17a0e4eb | ||
|
|
8163100559 | ||
|
|
1fec2bba06 | ||
|
|
706a340407 | ||
|
|
7e81484932 | ||
|
|
d7d8351dc9 | ||
|
|
19657fd150 | ||
|
|
a6cef76ecf | ||
|
|
ee8e0e531e | ||
|
|
151dc31f2c | ||
|
|
0b1a4a53a5 | ||
|
|
02efac9a8e | ||
|
|
8d739dd863 | ||
|
|
c0c6c6ed1c | ||
|
|
d26c67f06d | ||
|
|
35d60642c0 | ||
|
|
a928577f2a | ||
|
|
4b93e5fd67 | ||
|
|
d6454f51c8 | ||
|
|
efc0f49b66 | ||
|
|
a860da96a0 | ||
|
|
692eb3d2a3 | ||
|
|
b6be09ef7c | ||
|
|
8e15c8126f | ||
|
|
80a42d5d86 | ||
|
|
b8f3886a1b | ||
|
|
5a80041ec3 | ||
|
|
d76e7e2bf5 | ||
|
|
4fa3e16ab7 | ||
|
|
9cc3a2149e | ||
|
|
fdf52c0548 | ||
|
|
a6ee231ad5 | ||
|
|
4bcc6e4347 | ||
|
|
9395104c20 | ||
|
|
f063c4d165 | ||
|
|
b3b077fa9d | ||
|
|
88123e1edb | ||
|
|
0106311a2a | ||
|
|
f5f2d5743f | ||
|
|
4e38ad8e20 | ||
|
|
41362a591c | ||
|
|
1b36a45252 | ||
|
|
38db2e1310 | ||
|
|
9a3e87d89c | ||
|
|
954b5d3430 | ||
|
|
6dfe59c5a8 | ||
|
|
b741cbf9ba | ||
|
|
d5fbc490ea | ||
|
|
276ce5cae8 | ||
|
|
3a9312654e | ||
|
|
0a10009613 | ||
|
|
8759b660f5 | ||
|
|
ae3b9c8226 | ||
|
|
faec599822 | ||
|
|
da1c7cd8cf | ||
|
|
a0bf323ac9 | ||
|
|
4a2014e10d | ||
|
|
a01a64116f | ||
|
|
f6f2590532 | ||
|
|
20f5484e90 | ||
|
|
14f5bbfec9 | ||
|
|
5d5b04ba05 | ||
|
|
0ef2859291 | ||
|
|
b82d32cad7 | ||
|
|
10da3b8e77 | ||
|
|
184a80436d | ||
|
|
2de4d3548d | ||
|
|
fc197ea41a | ||
|
|
43385780f8 | ||
|
|
791102c034 | ||
|
|
8bcb3e74b6 | ||
|
|
e4c26a07cf | ||
|
|
f118c6b6f5 | ||
|
|
cb947429fb | ||
|
|
b3d620a329 | ||
|
|
28ffd134df | ||
|
|
f600245d3b | ||
|
|
706d811087 | ||
|
|
e5dd3ade51 | ||
|
|
83719f5f44 | ||
|
|
082817d6cd | ||
|
|
ee79e3eefa | ||
|
|
6a1084eeca | ||
|
|
71e6d24d6e | ||
|
|
7686a9b7d1 | ||
|
|
4837c17c2e | ||
|
|
3d367711cc | ||
|
|
6f006f38a4 | ||
|
|
cb4aeaed21 | ||
|
|
791baf474c | ||
|
|
468de4f439 | ||
|
|
2ec4f634ae | ||
|
|
8f7e61dfd2 | ||
|
|
212c2fa5dc | ||
|
|
dd88e8d1b8 | ||
|
|
eaf0f98058 | ||
|
|
1acc73c7b4 | ||
|
|
23df74ddac | ||
|
|
70478584b7 | ||
|
|
90295f0d38 | ||
|
|
5ff726d834 | ||
|
|
ab1bc844ab | ||
|
|
167b157134 | ||
|
|
3b98fc4de3 | ||
|
|
4a32fd216b | ||
|
|
af9b0d7cf2 | ||
|
|
c82d718fa7 | ||
|
|
fc3df592bc | ||
|
|
5ce42bf651 | ||
|
|
6a8acd4717 | ||
|
|
03f6f2743f | ||
|
|
13c42ddf38 | ||
|
|
c13913f08a | ||
|
|
b7ce12ff5e | ||
|
|
dd7c578534 | ||
|
|
d473ff81bd | ||
|
|
c6bfca0482 | ||
|
|
b1e7f3e485 | ||
|
|
a85ea56bb7 | ||
|
|
ee670f86b6 | ||
|
|
85b48475de | ||
|
|
0af3e7a449 | ||
|
|
2b54a7a13a | ||
|
|
4cc4866db1 | ||
|
|
a3d5e93240 | ||
|
|
ed494413fd | ||
|
|
557234841d | ||
|
|
ea17ab2ddd | ||
|
|
20ef8a8514 | ||
|
|
124879f11f | ||
|
|
125938b8be | ||
|
|
2208a2d292 | ||
|
|
1deec533ad | ||
|
|
633cfed87b | ||
|
|
c51a6508e4 | ||
|
|
12c2e13505 | ||
|
|
372e71bc1c | ||
|
|
5fee5dd363 | ||
|
|
2ee73416e0 | ||
|
|
73a1536df7 | ||
|
|
8ebad65140 | ||
|
|
6896a1f8af | ||
|
|
b30035d5a8 | ||
|
|
6b0e781fdf | ||
|
|
fd6ceb82df | ||
|
|
546f04dc0e | ||
|
|
bc11034892 | ||
|
|
dfe8b27a12 | ||
|
|
5eb7e0018a | ||
|
|
b3015084a6 | ||
|
|
316fd140bc | ||
|
|
3cb9bcc148 | ||
|
|
4ca3bd44da | ||
|
|
39c1473c57 | ||
|
|
3c3beebfc5 | ||
|
|
9b3a24e37a | ||
|
|
182236964b | ||
|
|
a3920f9084 | ||
|
|
45aef8204a | ||
|
|
e2c385b559 | ||
|
|
4a942f1e77 | ||
|
|
fb541849b4 | ||
|
|
9e92fad8fa | ||
|
|
3617292409 | ||
|
|
a3fc61494e | ||
|
|
fa62180667 | ||
|
|
e60c1cf1c4 | ||
|
|
7e60e4a4be | ||
|
|
f3a9cb8641 | ||
|
|
c577797e57 | ||
|
|
3a7e4970d4 | ||
|
|
0e14bb9a45 | ||
|
|
2869a77abd | ||
|
|
913d1f395f | ||
|
|
abf06c71af | ||
|
|
afeab61468 | ||
|
|
f06e93ef06 | ||
|
|
71b368a5ac | ||
|
|
1a69b521e6 | ||
|
|
47233fbe58 | ||
|
|
5b5845db14 | ||
|
|
c752f13d22 | ||
|
|
427393a747 | ||
|
|
d587b32a0a | ||
|
|
25320fe521 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -2,5 +2,6 @@
|
|||||||
thewhitesilk_config.json
|
thewhitesilk_config.json
|
||||||
# **默认**数据目录
|
# **默认**数据目录
|
||||||
thewhitesilk_data/
|
thewhitesilk_data/
|
||||||
|
# Node.js
|
||||||
deno.lock
|
package-lock.json
|
||||||
|
node_modules/
|
||||||
|
|||||||
8
.vscode/launch.json
vendored
8
.vscode/launch.json
vendored
@@ -5,10 +5,10 @@
|
|||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"command": "deno task debug",
|
"command": "npm run debug",
|
||||||
"name": "Run deno task debug",
|
"name": "Debug",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "node-terminal",
|
"type": "node-terminal"
|
||||||
},
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
22
Dockerfile
Normal file
22
Dockerfile
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# 使用官方 Deno 镜像
|
||||||
|
FROM denoland/deno:latest
|
||||||
|
|
||||||
|
# 设置镜像名称
|
||||||
|
LABEL image.name="lingchair"
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 复制源代码
|
||||||
|
COPY --exclude=.git --exclude=.gitignore --exclude=Dockerfile --exclude=readme.md --exclude=thewhitesilk_config.json --exclude=thewhitesilk_data . .
|
||||||
|
|
||||||
|
# 缓存依赖并构建项目
|
||||||
|
RUN npm run install-dependencies
|
||||||
|
|
||||||
|
RUN npm run build-client
|
||||||
|
|
||||||
|
# 暴露应用端口(根据你的应用调整端口号)
|
||||||
|
EXPOSE 3601
|
||||||
|
|
||||||
|
# 启动服务
|
||||||
|
CMD ["npm", "run", "server"]
|
||||||
3
client-protocol/ApiCallbackMessage.ts
Normal file
3
client-protocol/ApiCallbackMessage.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { ApiCallbackMessage } from 'lingchair-internal-shared'
|
||||||
|
|
||||||
|
export type { ApiCallbackMessage as default }
|
||||||
11
client-protocol/ApiDeclare.ts
Normal file
11
client-protocol/ApiDeclare.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export * from 'lingchair-internal-shared'
|
||||||
|
|
||||||
|
import { ClientEvent } from "lingchair-internal-shared"
|
||||||
|
|
||||||
|
import Message from "./Message.ts"
|
||||||
|
|
||||||
|
export type ClientEventData<T extends ClientEvent> =
|
||||||
|
T extends "Client.onMessage" ? { message: Message } :
|
||||||
|
never
|
||||||
|
|
||||||
|
export type ClientEventCallback<T extends ClientEvent> = (data: ClientEventData<T>) => void
|
||||||
8
client-protocol/BaseClientObject.ts
Normal file
8
client-protocol/BaseClientObject.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import LingChairClient from "./LingChairClient.ts"
|
||||||
|
|
||||||
|
export default class BaseClientObject {
|
||||||
|
declare client: LingChairClient
|
||||||
|
constructor(client: LingChairClient) {
|
||||||
|
this.client = client
|
||||||
|
}
|
||||||
|
}
|
||||||
11
client-protocol/CallbackError.ts
Normal file
11
client-protocol/CallbackError.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import ApiCallbackMessage from "./ApiCallbackMessage.ts"
|
||||||
|
|
||||||
|
export default class CallbackError extends Error {
|
||||||
|
declare code: number
|
||||||
|
declare data?: object
|
||||||
|
constructor(re: ApiCallbackMessage) {
|
||||||
|
super(`[${re.code}] ${re.msg}${re.data ? ` (data: ${JSON.stringify(re.data)})` : ''}`)
|
||||||
|
this.code = re.code
|
||||||
|
this.data = re.data
|
||||||
|
}
|
||||||
|
}
|
||||||
234
client-protocol/Chat.ts
Normal file
234
client-protocol/Chat.ts
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
import BaseClientObject from "./BaseClientObject.ts"
|
||||||
|
import BaseChatSettingsBean from "./bean/BaseChatSettingsBean.ts"
|
||||||
|
import ChatBean from "./bean/ChatBean.ts"
|
||||||
|
import JoinRequestBean from "./bean/JoinRequestBean.ts"
|
||||||
|
import MessageBean from "./bean/MessageBean.ts"
|
||||||
|
import CallbackError from "./CallbackError.ts"
|
||||||
|
import JoinRequest from "./JoinRequest.ts"
|
||||||
|
import LingChairClient from "./LingChairClient.ts"
|
||||||
|
import Message from "./Message.ts"
|
||||||
|
|
||||||
|
export default class Chat extends BaseClientObject {
|
||||||
|
declare bean: ChatBean
|
||||||
|
constructor(client: LingChairClient, bean: ChatBean) {
|
||||||
|
super(client)
|
||||||
|
this.bean = bean
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* ================================================
|
||||||
|
* 实例化方法
|
||||||
|
* ================================================
|
||||||
|
*/
|
||||||
|
static getForInvokeOnlyById(client: LingChairClient, id: string) {
|
||||||
|
return new Chat(client, {
|
||||||
|
id
|
||||||
|
} as ChatBean)
|
||||||
|
}
|
||||||
|
static async getById(client: LingChairClient, id: string) {
|
||||||
|
try {
|
||||||
|
return await this.getByIdOrThrow(client, id)
|
||||||
|
} catch (_) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static async getByIdOrThrow(client: LingChairClient, id: string) {
|
||||||
|
const re = await client.invoke("Chat.getInfo", {
|
||||||
|
token: client.access_token,
|
||||||
|
target: id,
|
||||||
|
})
|
||||||
|
if (re.code == 200)
|
||||||
|
return new Chat(client, re.data as unknown as ChatBean)
|
||||||
|
throw new CallbackError(re)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* ================================================
|
||||||
|
* 创建对话 (另类实例化方法)
|
||||||
|
* ================================================
|
||||||
|
*/
|
||||||
|
static async getOrCreatePrivateChat(client: LingChairClient, user_id: string) {
|
||||||
|
try {
|
||||||
|
return await this.getOrCreatePrivateChatOrThrow(client, user_id)
|
||||||
|
} catch (_) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static async getOrCreatePrivateChatOrThrow(client: LingChairClient, user_id: string) {
|
||||||
|
const re = await client.invoke("Chat.getIdForPrivate", {
|
||||||
|
token: client.access_token,
|
||||||
|
target: user_id,
|
||||||
|
})
|
||||||
|
if (re.code != 200) throw new CallbackError(re)
|
||||||
|
return new Chat(client, re.data as unknown as ChatBean)
|
||||||
|
}
|
||||||
|
static async createGroup(client: LingChairClient, title: string, name?: string) {
|
||||||
|
try {
|
||||||
|
return await this.createGroupOrThrow(client, title, name)
|
||||||
|
} catch (_) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static async createGroupOrThrow(client: LingChairClient, title: string, name?: string) {
|
||||||
|
const re = await client.invoke("Chat.createGroup", {
|
||||||
|
token: client.access_token,
|
||||||
|
title,
|
||||||
|
name,
|
||||||
|
})
|
||||||
|
if (re.code != 200) throw new CallbackError(re)
|
||||||
|
return new Chat(client, re.data as unknown as ChatBean)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* ================================================
|
||||||
|
* 对话消息
|
||||||
|
* ================================================
|
||||||
|
*/
|
||||||
|
async getMessages(page: number = 0) {
|
||||||
|
return (await this.getMessageBeans(page)).map((v) => new Message(this.client, v))
|
||||||
|
}
|
||||||
|
async getMessagesOrThrow(page: number = 0) {
|
||||||
|
return (await this.getMessageBeansOrThrow(page)).map((v) => new Message(this.client, v))
|
||||||
|
}
|
||||||
|
async getMessageBeans(page: number = 0) {
|
||||||
|
try {
|
||||||
|
return await this.getMessageBeansOrThrow(page)
|
||||||
|
} catch (_) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getMessageBeansOrThrow(page: number = 0) {
|
||||||
|
const re = await this.client.invoke("Chat.getMessageHistory", {
|
||||||
|
token: this.client.access_token,
|
||||||
|
page,
|
||||||
|
target: this.bean.id,
|
||||||
|
})
|
||||||
|
if (re.code == 200) return re.data!.messages as MessageBean[]
|
||||||
|
throw new CallbackError(re)
|
||||||
|
}
|
||||||
|
async sendMessage(text: string) {
|
||||||
|
try {
|
||||||
|
return await this.sendMessageOrThrow(text)
|
||||||
|
} catch (_) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async sendMessageOrThrow(text: string) {
|
||||||
|
const re = await this.client.invoke("Chat.sendMessage", {
|
||||||
|
token: this.client.access_token,
|
||||||
|
text,
|
||||||
|
target: this.bean.id,
|
||||||
|
})
|
||||||
|
if (re.code == 200)
|
||||||
|
return new Message(this.client, re.data!.message as MessageBean)
|
||||||
|
throw new CallbackError(re)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* ================================================
|
||||||
|
* 加入对话申请
|
||||||
|
* ================================================
|
||||||
|
*/
|
||||||
|
async getJoinRequests() {
|
||||||
|
try {
|
||||||
|
return await this.getJoinRequestsOrThrow()
|
||||||
|
} catch (_) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getJoinRequestsOrThrow() {
|
||||||
|
const join_requests = await this.getJoinRequestBeansOrThrow()
|
||||||
|
return join_requests.map((jr) => new JoinRequest(this.client, jr, this.bean.id))
|
||||||
|
}
|
||||||
|
async getJoinRequestBeans() {
|
||||||
|
try {
|
||||||
|
return await this.getJoinRequestBeansOrThrow()
|
||||||
|
} catch (_) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getJoinRequestBeansOrThrow() {
|
||||||
|
const re = await this.client.invoke("Chat.getJoinRequests", {
|
||||||
|
token: this.client.access_token
|
||||||
|
})
|
||||||
|
if (re.code == 200)
|
||||||
|
return re.data!.join_requests as JoinRequestBean[]
|
||||||
|
throw new CallbackError(re)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* ================================================
|
||||||
|
* 对话信息
|
||||||
|
* ================================================
|
||||||
|
*/
|
||||||
|
async setAvatarFileHash(file_hash: string) {
|
||||||
|
try {
|
||||||
|
await this.setAvatarFileHashOrThrow(file_hash)
|
||||||
|
return true
|
||||||
|
} catch (_) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async setAvatarFileHashOrThrow(file_hash: string) {
|
||||||
|
const re = await this.client.invoke("Chat.setAvatar", {
|
||||||
|
token: this.client.access_token,
|
||||||
|
file_hash,
|
||||||
|
target: this.bean.id,
|
||||||
|
})
|
||||||
|
if (re.code != 200) throw new CallbackError(re)
|
||||||
|
this.bean.avatar_file_hash = file_hash
|
||||||
|
}
|
||||||
|
async updateSettings(args: BaseChatSettingsBean) {
|
||||||
|
try {
|
||||||
|
await this.updateSettingsOrThrow(args)
|
||||||
|
return true
|
||||||
|
} catch (_) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async updateSettingsOrThrow(args: BaseChatSettingsBean) {
|
||||||
|
const re = await this.client.invoke("Chat.updateSettings", {
|
||||||
|
token: this.client.access_token,
|
||||||
|
target: this.bean.id,
|
||||||
|
settings: args
|
||||||
|
})
|
||||||
|
if (re.code != 200) throw new CallbackError(re)
|
||||||
|
this.bean.settings = args
|
||||||
|
}
|
||||||
|
async getTheOtherUserId() {
|
||||||
|
try {
|
||||||
|
return await this.getTheOtherUserIdOrThrow()
|
||||||
|
} catch (_) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getTheOtherUserIdOrThrow() {
|
||||||
|
const re = await this.client.invoke("Chat.updateSettings", {
|
||||||
|
token: this.client.access_token,
|
||||||
|
target: this.bean.id,
|
||||||
|
})
|
||||||
|
if (re.code != 200) throw new CallbackError(re)
|
||||||
|
return re.data!.user_id as string
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* ================================================
|
||||||
|
* 基本 Bean
|
||||||
|
* ================================================
|
||||||
|
*/
|
||||||
|
getId() {
|
||||||
|
return this.bean.id
|
||||||
|
}
|
||||||
|
getTitle() {
|
||||||
|
return this.bean.title
|
||||||
|
}
|
||||||
|
getType() {
|
||||||
|
return this.bean.type
|
||||||
|
}
|
||||||
|
isMember() {
|
||||||
|
return this.bean.is_member
|
||||||
|
}
|
||||||
|
isAdmin() {
|
||||||
|
return this.bean.is_admin
|
||||||
|
}
|
||||||
|
getAvatarFileHash() {
|
||||||
|
return this.bean.avatar_file_hash
|
||||||
|
}
|
||||||
|
getSettings() {
|
||||||
|
return this.bean.settings
|
||||||
|
}
|
||||||
|
}
|
||||||
66
client-protocol/JoinRequest.ts
Normal file
66
client-protocol/JoinRequest.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import BaseClientObject from "./BaseClientObject.ts"
|
||||||
|
import JoinRequestBean from "./bean/JoinRequestBean.ts"
|
||||||
|
import CallbackError from "./CallbackError.ts"
|
||||||
|
import LingChairClient from "./LingChairClient.ts"
|
||||||
|
import JoinRequestAction from "./type/JoinRequestAction.ts"
|
||||||
|
|
||||||
|
export default class JoinRequest extends BaseClientObject {
|
||||||
|
declare bean: JoinRequestBean
|
||||||
|
declare chat_id: string
|
||||||
|
constructor(client: LingChairClient, bean: JoinRequestBean, chat_id: string) {
|
||||||
|
super(client)
|
||||||
|
this.bean = bean
|
||||||
|
this.chat_id = chat_id
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* ================================================
|
||||||
|
* 操作
|
||||||
|
* ================================================
|
||||||
|
*/
|
||||||
|
async accept() {
|
||||||
|
return await this.process('accept')
|
||||||
|
}
|
||||||
|
async acceptOrThrow() {
|
||||||
|
return await this.processOrThrow('accept')
|
||||||
|
}
|
||||||
|
async remove() {
|
||||||
|
return await this.process('remove')
|
||||||
|
}
|
||||||
|
async removOrThrow() {
|
||||||
|
return await this.processOrThrow('remove')
|
||||||
|
}
|
||||||
|
async process(action: JoinRequestAction) {
|
||||||
|
try {
|
||||||
|
await this.processOrThrow(action)
|
||||||
|
return true
|
||||||
|
} catch (_) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async processOrThrow(action: JoinRequestAction) {
|
||||||
|
const re = await this.client.invoke("Chat.processJoinRequest", {
|
||||||
|
token: this.client.access_token,
|
||||||
|
chat_id: this.chat_id,
|
||||||
|
user_id: this.bean.user_id,
|
||||||
|
action,
|
||||||
|
})
|
||||||
|
if (re.code != 200) throw new CallbackError(re)
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* ================================================
|
||||||
|
* 基本 Bean
|
||||||
|
* ================================================
|
||||||
|
*/
|
||||||
|
getAvatarFileHash() {
|
||||||
|
return this.bean.avatar_file_hash
|
||||||
|
}
|
||||||
|
getUserId() {
|
||||||
|
return this.bean.user_id
|
||||||
|
}
|
||||||
|
getNickName() {
|
||||||
|
return this.bean.title
|
||||||
|
}
|
||||||
|
getReason() {
|
||||||
|
return this.bean.reason
|
||||||
|
}
|
||||||
|
}
|
||||||
269
client-protocol/LingChairClient.ts
Normal file
269
client-protocol/LingChairClient.ts
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
// deno-lint-ignore-file no-explicit-any
|
||||||
|
import { io, ManagerOptions, Socket, SocketOptions } from 'socket.io-client'
|
||||||
|
import crypto from 'node:crypto'
|
||||||
|
import { CallMethod, ClientEvent, ClientEventCallback } from './ApiDeclare.ts'
|
||||||
|
import ApiCallbackMessage from './ApiCallbackMessage.ts'
|
||||||
|
import { CallableMethodBeforeAuth, randomUUID } from "lingchair-internal-shared"
|
||||||
|
import CallbackError from "./CallbackError.ts"
|
||||||
|
|
||||||
|
import Message from "./Message.ts"
|
||||||
|
|
||||||
|
export default class LingChairClient {
|
||||||
|
declare client: Socket
|
||||||
|
declare access_token: string
|
||||||
|
declare server_url: string
|
||||||
|
declare device_id: string
|
||||||
|
declare refresh_token?: string
|
||||||
|
declare auto_fresh_token: boolean
|
||||||
|
declare auth_cache: {
|
||||||
|
refresh_token?: string,
|
||||||
|
access_token?: string,
|
||||||
|
account?: string,
|
||||||
|
password?: string,
|
||||||
|
}
|
||||||
|
constructor(args: {
|
||||||
|
server_url: string
|
||||||
|
device_id: string,
|
||||||
|
io?: Partial<ManagerOptions & SocketOptions>
|
||||||
|
auto_fresh_token?: boolean
|
||||||
|
}) {
|
||||||
|
this.server_url = args.server_url
|
||||||
|
this.auto_fresh_token = args.auto_fresh_token || false
|
||||||
|
this.device_id = args.device_id
|
||||||
|
this.client = io(args.server_url, {
|
||||||
|
transports: ["polling", "websocket", "webtransport"],
|
||||||
|
...args.io,
|
||||||
|
auth: {
|
||||||
|
...args.io?.auth,
|
||||||
|
device_id: this.device_id,
|
||||||
|
session_id: randomUUID(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
this.client.on("The_White_Silk", (name: ClientEvent, data: any, _callback: (ret: unknown) => void) => {
|
||||||
|
try {
|
||||||
|
if (name == null || data == null) return
|
||||||
|
for (const v of (this.events[name] || []))
|
||||||
|
v(({
|
||||||
|
"Client.onMessage": {
|
||||||
|
message: new Message(this, data.msg)
|
||||||
|
}
|
||||||
|
})[name])
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
events: { [K in ClientEvent]?: ClientEventCallback<K>[] } = {}
|
||||||
|
on<K extends ClientEvent>(eventName: K, func: ClientEventCallback<K>) {
|
||||||
|
if (this.events[eventName] == null)
|
||||||
|
this.events[eventName] = []
|
||||||
|
if (this.events[eventName].indexOf(func) == -1)
|
||||||
|
this.events[eventName].push(func)
|
||||||
|
}
|
||||||
|
off<K extends ClientEvent>(eventName: K, func: ClientEventCallback<K>) {
|
||||||
|
if (this.events[eventName] == null)
|
||||||
|
this.events[eventName] = []
|
||||||
|
const index = this.events[eventName].indexOf(func)
|
||||||
|
if (index != -1)
|
||||||
|
this.events[eventName].splice(index, 1)
|
||||||
|
}
|
||||||
|
connect() {
|
||||||
|
this.client.connect()
|
||||||
|
}
|
||||||
|
disconnect() {
|
||||||
|
this.client.disconnect()
|
||||||
|
}
|
||||||
|
reconnect() {
|
||||||
|
this.disconnect()
|
||||||
|
this.connect()
|
||||||
|
}
|
||||||
|
invoke(method: CallMethod, args: object = {}, timeout: number = 10000): Promise<ApiCallbackMessage> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
this.client!.timeout(timeout).emit("The_White_Silk", method, args, (err: Error, res: ApiCallbackMessage) => {
|
||||||
|
// 错误处理
|
||||||
|
if (err) return resolve({
|
||||||
|
code: -1,
|
||||||
|
msg: err.message,
|
||||||
|
})
|
||||||
|
if (CallableMethodBeforeAuth.indexOf(method) == -1 && res.code == 401 && this.auto_fresh_token) {
|
||||||
|
if (this.auth_cache)
|
||||||
|
this.auth(this.auth_cache).then((re) => {
|
||||||
|
if (!re) resolve(res)
|
||||||
|
this.invoke(method, args, timeout).then((re) => resolve(re))
|
||||||
|
})
|
||||||
|
else
|
||||||
|
resolve(res)
|
||||||
|
} else
|
||||||
|
resolve(res)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 建议在 auth 返回 true 时调用
|
||||||
|
*/
|
||||||
|
getCachedAccessToken() {
|
||||||
|
return this.access_token
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 建议在 auth 返回 true 时调用
|
||||||
|
*/
|
||||||
|
getCachedRefreshToken() {
|
||||||
|
return this.refresh_token
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 客户端上线
|
||||||
|
*
|
||||||
|
* 使用验证方式优先级: 访问 > 刷新 > 账号密码
|
||||||
|
*
|
||||||
|
* 不会逐一尝试
|
||||||
|
*/
|
||||||
|
async auth(args: {
|
||||||
|
refresh_token?: string,
|
||||||
|
access_token?: string,
|
||||||
|
account?: string,
|
||||||
|
password?: string,
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
await this.authOrThrow(args)
|
||||||
|
return true
|
||||||
|
} catch (_) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 进行身份验证以接受客户端事件
|
||||||
|
*
|
||||||
|
* 使用验证方式优先级: 访问 > 刷新 > 账号密码
|
||||||
|
*
|
||||||
|
* 若传递了账号密码, 则同时缓存新的访问令牌和刷新令牌
|
||||||
|
*
|
||||||
|
* 如只传递两个令牌的其一, 按照优先级并在成功验证后赋值
|
||||||
|
*
|
||||||
|
* 多个验证方式不会逐一尝试
|
||||||
|
*/
|
||||||
|
async authOrThrow(args: {
|
||||||
|
refresh_token?: string
|
||||||
|
access_token?: string
|
||||||
|
account?: string
|
||||||
|
password?: string
|
||||||
|
ignore_all_empty?: boolean
|
||||||
|
}) {
|
||||||
|
if ((!args.access_token && !args.refresh_token) && (!args.account && !args.password) && !args.ignore_all_empty)
|
||||||
|
throw new Error('Access/Refresh token or account & password required, or ignore_all_empty=true')
|
||||||
|
|
||||||
|
this.auth_cache = args
|
||||||
|
|
||||||
|
let access_token = args.access_token
|
||||||
|
if (!access_token && args.refresh_token) {
|
||||||
|
const re = await this.invoke('User.refreshAccessToken', {
|
||||||
|
refresh_token: args.refresh_token,
|
||||||
|
})
|
||||||
|
if (re.code == 200) {
|
||||||
|
access_token = re.data!.access_token as string | undefined
|
||||||
|
this.refresh_token = args.refresh_token
|
||||||
|
} else
|
||||||
|
throw new CallbackError(re)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!access_token && (args.account && args.password)) {
|
||||||
|
const re = await this.invoke('User.login', {
|
||||||
|
account: args.account,
|
||||||
|
password: crypto.createHash('sha256').update(args.password).digest('hex'),
|
||||||
|
})
|
||||||
|
if (re.code == 200) {
|
||||||
|
access_token = re.data!.access_token as string | undefined
|
||||||
|
this.refresh_token = re.data!.refresh_token as string
|
||||||
|
} else
|
||||||
|
throw new CallbackError(re)
|
||||||
|
}
|
||||||
|
|
||||||
|
const re = await this.invoke('User.auth', {
|
||||||
|
access_token: access_token
|
||||||
|
})
|
||||||
|
if (re.code == 200)
|
||||||
|
this.access_token = access_token as string
|
||||||
|
else
|
||||||
|
throw new CallbackError(re)
|
||||||
|
}
|
||||||
|
getBaseHttpUrl() {
|
||||||
|
const url = new URL(this.server_url)
|
||||||
|
return (({
|
||||||
|
'ws:': 'http:',
|
||||||
|
'wss:': 'https:',
|
||||||
|
'http:': 'http:',
|
||||||
|
'https:': 'https:',
|
||||||
|
})[url.protocol] || 'http:') + '//' + url.host
|
||||||
|
}
|
||||||
|
getUrlForFileByHash(file_hash?: string, defaultUrl?: string) {
|
||||||
|
return file_hash ? (this.getBaseHttpUrl() + '/uploaded_files/' + file_hash) : defaultUrl
|
||||||
|
}
|
||||||
|
async register(args: {
|
||||||
|
nickname: string,
|
||||||
|
username?: string,
|
||||||
|
password: string,
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
return await this.registerOrThrow(args)
|
||||||
|
} catch (_) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async registerOrThrow({
|
||||||
|
nickname,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
}: {
|
||||||
|
nickname: string,
|
||||||
|
username?: string,
|
||||||
|
password: string,
|
||||||
|
}) {
|
||||||
|
const re = await this.invoke('User.register', {
|
||||||
|
nickname,
|
||||||
|
username,
|
||||||
|
password: crypto.createHash('sha256').update(password).digest('hex'),
|
||||||
|
})
|
||||||
|
if (re.code != 200)
|
||||||
|
throw new CallbackError(re)
|
||||||
|
return re.data!.user_id as string
|
||||||
|
}
|
||||||
|
async uploadFile({
|
||||||
|
chatId,
|
||||||
|
fileData,
|
||||||
|
fileName,
|
||||||
|
}: { fileName: string, fileData: ArrayBuffer | Blob | Response, chatId?: string }) {
|
||||||
|
const form = new FormData()
|
||||||
|
form.append("file",
|
||||||
|
fileData instanceof ArrayBuffer
|
||||||
|
? new File([fileData], fileName, { type: 'application/octet-stream' })
|
||||||
|
: (
|
||||||
|
fileData instanceof Blob ? fileData :
|
||||||
|
new File([await fileData.arrayBuffer()], fileName, { type: 'application/octet-stream' })
|
||||||
|
)
|
||||||
|
)
|
||||||
|
form.append('file_name', fileName)
|
||||||
|
chatId && form.append('chat_id', chatId)
|
||||||
|
|
||||||
|
const re = await fetch(this.getBaseHttpUrl() + '/upload_file', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
"Token": this.access_token,
|
||||||
|
"Device-Id": this.device_id,
|
||||||
|
} as HeadersInit,
|
||||||
|
body: form,
|
||||||
|
credentials: 'omit',
|
||||||
|
})
|
||||||
|
const text = await (await re.blob()).text()
|
||||||
|
let json
|
||||||
|
try {
|
||||||
|
json = JSON.parse(text)
|
||||||
|
// deno-lint-ignore no-empty
|
||||||
|
} catch (_) { }
|
||||||
|
if (!re.ok) throw new CallbackError({
|
||||||
|
...(json == null ? {
|
||||||
|
msg: text
|
||||||
|
} : json),
|
||||||
|
code: re.status,
|
||||||
|
} as ApiCallbackMessage)
|
||||||
|
return json.data.file_hash as string
|
||||||
|
}
|
||||||
|
}
|
||||||
240
client-protocol/Message.ts
Normal file
240
client-protocol/Message.ts
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
import BaseClientObject from "./BaseClientObject.ts"
|
||||||
|
import MessageBean from "./bean/MessageBean.ts"
|
||||||
|
import LingChairClient from "./LingChairClient.ts"
|
||||||
|
import Chat from "./Chat.ts"
|
||||||
|
import User from "./User.ts"
|
||||||
|
import CallbackError from "./CallbackError.ts"
|
||||||
|
import ApiCallbackMessage from "./ApiCallbackMessage.ts"
|
||||||
|
|
||||||
|
import * as marked from 'marked'
|
||||||
|
import { text } from "node:stream/consumers";
|
||||||
|
|
||||||
|
class ChatMention extends BaseClientObject {
|
||||||
|
declare chat_id?: string
|
||||||
|
declare user_id?: string
|
||||||
|
declare text?: string
|
||||||
|
constructor(client: LingChairClient, {
|
||||||
|
user_id,
|
||||||
|
chat_id,
|
||||||
|
text,
|
||||||
|
}: {
|
||||||
|
user_id?: string,
|
||||||
|
chat_id?: string,
|
||||||
|
text: string,
|
||||||
|
}) {
|
||||||
|
super(client)
|
||||||
|
this.user_id = user_id
|
||||||
|
this.chat_id = chat_id
|
||||||
|
this.text = text
|
||||||
|
}
|
||||||
|
async getChat() {
|
||||||
|
return await Chat.getById(this.client, this.chat_id as string)
|
||||||
|
}
|
||||||
|
async getUser() {
|
||||||
|
return await User.getById(this.client, this.user_id as string)
|
||||||
|
}
|
||||||
|
getText() {
|
||||||
|
return this.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileType = 'Video' | 'Image' | 'File'
|
||||||
|
type MentionType = 'ChatMention' | 'UserMention'
|
||||||
|
|
||||||
|
class ChatAttachment extends BaseClientObject {
|
||||||
|
declare file_hash: string
|
||||||
|
declare file_name: string
|
||||||
|
constructor(client: LingChairClient, {
|
||||||
|
file_hash,
|
||||||
|
file_name
|
||||||
|
}: {
|
||||||
|
file_hash: string,
|
||||||
|
file_name: string
|
||||||
|
}) {
|
||||||
|
super(client)
|
||||||
|
this.file_name = file_name
|
||||||
|
this.file_hash = file_hash
|
||||||
|
}
|
||||||
|
async blob() {
|
||||||
|
try {
|
||||||
|
return await this.blobOrThrow()
|
||||||
|
} catch (_) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fetch(init?: RequestInit) {
|
||||||
|
const url = this.client.getUrlForFileByHash(this.file_hash)
|
||||||
|
return fetch(url!, init)
|
||||||
|
}
|
||||||
|
async blobOrThrow() {
|
||||||
|
const re = await this.fetch()
|
||||||
|
const blob = await re.blob()
|
||||||
|
if (!re.ok) throw new CallbackError({
|
||||||
|
msg: await blob.text(),
|
||||||
|
code: re.status,
|
||||||
|
} as ApiCallbackMessage)
|
||||||
|
return blob
|
||||||
|
}
|
||||||
|
async getMimeType() {
|
||||||
|
try {
|
||||||
|
return await this.getMimeTypeOrThrow()
|
||||||
|
} catch (_) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getMimeTypeOrThrow() {
|
||||||
|
const re = await this.fetch({
|
||||||
|
method: 'HEAD'
|
||||||
|
})
|
||||||
|
if (re.ok) {
|
||||||
|
const t = re.headers.get('content-type')
|
||||||
|
if (t)
|
||||||
|
return t
|
||||||
|
throw new Error("Unable to get Content-Type")
|
||||||
|
}
|
||||||
|
throw new CallbackError({
|
||||||
|
msg: await re.text(),
|
||||||
|
code: re.status,
|
||||||
|
} as ApiCallbackMessage)
|
||||||
|
}
|
||||||
|
async getLength() {
|
||||||
|
try {
|
||||||
|
return await this.getLengthOrThrow()
|
||||||
|
} catch (_) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getLengthOrThrow() {
|
||||||
|
const re = await this.fetch({
|
||||||
|
method: 'HEAD'
|
||||||
|
})
|
||||||
|
if (re.ok) {
|
||||||
|
const contentLength = re.headers.get('content-length')
|
||||||
|
if (contentLength)
|
||||||
|
return parseInt(contentLength)
|
||||||
|
throw new Error("Unable to get Content-Length")
|
||||||
|
}
|
||||||
|
throw new CallbackError({
|
||||||
|
msg: await re.text(),
|
||||||
|
code: re.status,
|
||||||
|
} as ApiCallbackMessage)
|
||||||
|
}
|
||||||
|
getFileHash() {
|
||||||
|
return this.file_hash
|
||||||
|
}
|
||||||
|
getFileName() {
|
||||||
|
return this.file_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Message extends BaseClientObject {
|
||||||
|
declare bean: MessageBean
|
||||||
|
constructor(client: LingChairClient, bean: MessageBean) {
|
||||||
|
super(client)
|
||||||
|
this.bean = bean
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* ================================================
|
||||||
|
* 基本 Bean
|
||||||
|
* ================================================
|
||||||
|
*/
|
||||||
|
getId() {
|
||||||
|
return this.bean.id
|
||||||
|
}
|
||||||
|
getChatId() {
|
||||||
|
return this.bean.chat_id
|
||||||
|
}
|
||||||
|
async getChat() {
|
||||||
|
return await Chat.getById(this.client, this.bean.chat_id as string)
|
||||||
|
}
|
||||||
|
getText() {
|
||||||
|
return this.bean.text
|
||||||
|
}
|
||||||
|
parseWithTransformers({
|
||||||
|
attachment,
|
||||||
|
mention,
|
||||||
|
}: {
|
||||||
|
attachment?: ({ text, fileType, attachment }: { text: string, fileType: FileType, attachment: ChatAttachment }) => string,
|
||||||
|
mention?: ({ text, mentionType, mention }: { text: string, mentionType: MentionType, mention: ChatMention }) => string,
|
||||||
|
}) {
|
||||||
|
return new marked.Marked({
|
||||||
|
async: false,
|
||||||
|
extensions: [
|
||||||
|
{
|
||||||
|
name: 'text',
|
||||||
|
renderer: ({ text }) => text,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'heading',
|
||||||
|
renderer({ tokens }) {
|
||||||
|
return this.parser.parseInline(tokens!)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'paragraph',
|
||||||
|
renderer({ tokens }) {
|
||||||
|
return this.parser.parseInline(tokens!)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'image',
|
||||||
|
renderer: ({ text, href }) => {
|
||||||
|
const mentionType = /^(UserMention|ChatMention)=.*/.exec(text)?.[1] as MentionType
|
||||||
|
const fileType = (/^(Video|File|Image)=.*/.exec(text)?.[1] || 'Image') as FileType
|
||||||
|
|
||||||
|
if (fileType != null && /tws:\/\/file\?hash=[A-Za-z0-9]+$/.test(href)) {
|
||||||
|
const file_hash = /^tws:\/\/file\?hash=(.*)/.exec(href)?.[1]!
|
||||||
|
let file_name: string = /^(Video|File|Image)=(.*)/.exec(text)?.[2] || text
|
||||||
|
file_name.trim() == '' && (file_name = 'Unnamed_File')
|
||||||
|
return attachment ? attachment({ text: text, attachment: new ChatAttachment(this.client, { file_hash, file_name }), fileType: fileType, }) : text
|
||||||
|
}
|
||||||
|
if (mentionType != null && /^tws:\/\/chat\?id=[A-Za-z0-9]+/.test(href)) {
|
||||||
|
const id = /^tws:\/\/chat\?id=(.*)/.exec(href)?.[1]!
|
||||||
|
const label = /^(User|Chat)Mention=(.*)/.exec(text)?.[2] || ''
|
||||||
|
return mention ? mention({
|
||||||
|
text: text,
|
||||||
|
mention: new ChatMention(this.client, {
|
||||||
|
[({
|
||||||
|
ChatMention: 'chat_id',
|
||||||
|
UserMention: 'user_id',
|
||||||
|
})[mentionType]]: id,
|
||||||
|
text: label,
|
||||||
|
}),
|
||||||
|
mentionType: mentionType,
|
||||||
|
}) : text
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}).parse(this.getText()) as string
|
||||||
|
}
|
||||||
|
getAttachments() {
|
||||||
|
const attachments: ChatAttachment[] = []
|
||||||
|
this.parseWithTransformers({
|
||||||
|
attachment({ attachment }) {
|
||||||
|
attachments.push(attachment)
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return attachments
|
||||||
|
}
|
||||||
|
getMentions() {
|
||||||
|
const mentions: ChatMention[] = []
|
||||||
|
this.parseWithTransformers({
|
||||||
|
mention({ mention }) {
|
||||||
|
mentions.push(mention)
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return mentions
|
||||||
|
}
|
||||||
|
getUserId() {
|
||||||
|
return this.bean.user_id
|
||||||
|
}
|
||||||
|
async getUser() {
|
||||||
|
return await User.getById(this.client, this.bean.user_id as string)
|
||||||
|
}
|
||||||
|
getTime() {
|
||||||
|
return this.bean.time
|
||||||
|
}
|
||||||
|
}
|
||||||
13
client-protocol/RecentChat.ts
Normal file
13
client-protocol/RecentChat.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import RecentChatBean from "./bean/RecentChatBean.ts"
|
||||||
|
import Chat from "./Chat.ts"
|
||||||
|
import LingChairClient from "./LingChairClient.ts"
|
||||||
|
|
||||||
|
export default class RecentChat extends Chat {
|
||||||
|
declare bean: RecentChatBean
|
||||||
|
constructor(client: LingChairClient, bean: RecentChatBean) {
|
||||||
|
super(client, bean)
|
||||||
|
}
|
||||||
|
getContent() {
|
||||||
|
return this.bean.content
|
||||||
|
}
|
||||||
|
}
|
||||||
55
client-protocol/User.ts
Normal file
55
client-protocol/User.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import BaseClientObject from "./BaseClientObject.ts"
|
||||||
|
import UserBean from "./bean/UserBean.ts"
|
||||||
|
import CallbackError from "./CallbackError.ts"
|
||||||
|
import LingChairClient from "./LingChairClient.ts"
|
||||||
|
|
||||||
|
export default class User extends BaseClientObject {
|
||||||
|
declare bean: UserBean
|
||||||
|
constructor(client: LingChairClient, bean: UserBean) {
|
||||||
|
super(client)
|
||||||
|
this.bean = bean
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* ================================================
|
||||||
|
* 实例化方法
|
||||||
|
* ================================================
|
||||||
|
*/
|
||||||
|
static getForInvokeOnlyById(client: LingChairClient, id: string) {
|
||||||
|
return new User(client, {
|
||||||
|
id
|
||||||
|
} as UserBean)
|
||||||
|
}
|
||||||
|
static async getById(client: LingChairClient, id: string) {
|
||||||
|
try {
|
||||||
|
return await this.getByIdOrThrow(client, id)
|
||||||
|
} catch (_) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static async getByIdOrThrow(client: LingChairClient, id: string) {
|
||||||
|
const re = await client.invoke("User.getInfo", {
|
||||||
|
token: client.access_token,
|
||||||
|
target: id,
|
||||||
|
})
|
||||||
|
if (re.code == 200)
|
||||||
|
return new User(client, re.data as unknown as UserBean)
|
||||||
|
throw new CallbackError(re)
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* ================================================
|
||||||
|
* 基本 Bean
|
||||||
|
* ================================================
|
||||||
|
*/
|
||||||
|
getId() {
|
||||||
|
return this.bean.id
|
||||||
|
}
|
||||||
|
getUserName() {
|
||||||
|
return this.bean.username
|
||||||
|
}
|
||||||
|
getNickName() {
|
||||||
|
return this.bean.nickname
|
||||||
|
}
|
||||||
|
getAvatarFileHash() {
|
||||||
|
return this.bean.avatar_file_hash
|
||||||
|
}
|
||||||
|
}
|
||||||
232
client-protocol/UserMySelf.ts
Normal file
232
client-protocol/UserMySelf.ts
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
import CallbackError from "./CallbackError.ts"
|
||||||
|
import Chat from "./Chat.ts"
|
||||||
|
import LingChairClient from "./LingChairClient.ts"
|
||||||
|
import RecentChat from "./RecentChat.ts"
|
||||||
|
import User from "./User.ts"
|
||||||
|
import ChatBean from "./bean/ChatBean.ts"
|
||||||
|
import RecentChatBean from "./bean/RecentChatBean.ts"
|
||||||
|
import UserBean from "./bean/UserBean.ts"
|
||||||
|
|
||||||
|
export default class UserMySelf extends User {
|
||||||
|
/*
|
||||||
|
* ================================================
|
||||||
|
* 实例化方法
|
||||||
|
* ================================================
|
||||||
|
*/
|
||||||
|
static async getMySelf(client: LingChairClient) {
|
||||||
|
try {
|
||||||
|
return await this.getMySelfOrThrow(client)
|
||||||
|
} catch (_) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static async getMySelfOrThrow(client: LingChairClient) {
|
||||||
|
const re = await client.invoke("User.getMyInfo", {
|
||||||
|
token: client.access_token,
|
||||||
|
})
|
||||||
|
if (re.code == 200)
|
||||||
|
return new UserMySelf(client, re.data as unknown as UserBean)
|
||||||
|
throw new CallbackError(re)
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* ================================================
|
||||||
|
* 账号相关
|
||||||
|
* ================================================
|
||||||
|
*/
|
||||||
|
async resetPassword(old_password: string, new_password: string) {
|
||||||
|
try {
|
||||||
|
await this.resetPasswordOrThrow(old_password, new_password)
|
||||||
|
return true
|
||||||
|
} catch (_) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async resetPasswordOrThrow(old_password: string, new_password: string) {
|
||||||
|
const re = await this.client.invoke("User.resetPassword", {
|
||||||
|
token: this.client.access_token,
|
||||||
|
old_password,
|
||||||
|
new_password,
|
||||||
|
})
|
||||||
|
if (re.code != 200) throw new CallbackError(re)
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* ================================================
|
||||||
|
* 个人资料
|
||||||
|
* ================================================
|
||||||
|
*/
|
||||||
|
async setAvatarFileHash(file_hash: string) {
|
||||||
|
try {
|
||||||
|
await this.setAvatarFileHashOrThrow(file_hash)
|
||||||
|
return true
|
||||||
|
} catch (_) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async setAvatarFileHashOrThrow(file_hash: string) {
|
||||||
|
const re = await this.client.invoke("User.setAvatar", {
|
||||||
|
token: this.client.access_token,
|
||||||
|
file_hash,
|
||||||
|
})
|
||||||
|
if (re.code != 200) throw new CallbackError(re)
|
||||||
|
this.bean.avatar_file_hash = file_hash
|
||||||
|
}
|
||||||
|
async setUserName(user_name: string) {
|
||||||
|
return await this.updateProfile({ username: user_name })
|
||||||
|
}
|
||||||
|
async setUserNameOrThrow(user_name: string) {
|
||||||
|
await this.updateProfileOrThrow({ username: user_name })
|
||||||
|
}
|
||||||
|
async setNickName(nick_name: string) {
|
||||||
|
return await this.updateProfile({ nickname: nick_name })
|
||||||
|
}
|
||||||
|
async setNickNameOrThrow(nick_name: string) {
|
||||||
|
await this.updateProfileOrThrow({ nickname: nick_name })
|
||||||
|
}
|
||||||
|
async updateProfile(args: {
|
||||||
|
username?: string,
|
||||||
|
nickname?: string
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
await this.updateProfileOrThrow(args)
|
||||||
|
return true
|
||||||
|
} catch (_) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async updateProfileOrThrow({
|
||||||
|
username,
|
||||||
|
nickname
|
||||||
|
}: {
|
||||||
|
username?: string,
|
||||||
|
nickname?: string
|
||||||
|
}) {
|
||||||
|
const re = await this.client.invoke("User.updateProfile", {
|
||||||
|
token: this.client.access_token,
|
||||||
|
nickname,
|
||||||
|
username,
|
||||||
|
})
|
||||||
|
if (re.code != 200) throw new CallbackError(re)
|
||||||
|
nickname && (this.bean.nickname = nickname)
|
||||||
|
username && (this.bean.username = username)
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* ================================================
|
||||||
|
* 收藏对话
|
||||||
|
* ================================================
|
||||||
|
*/
|
||||||
|
async addFavouriteChats(chat_ids: string[]) {
|
||||||
|
try {
|
||||||
|
await this.addFavouriteChatsOrThrow(chat_ids)
|
||||||
|
return true
|
||||||
|
} catch (_) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async addFavouriteChatsOrThrow(chat_ids: string[]) {
|
||||||
|
const re = await this.client.invoke("User.addContacts", {
|
||||||
|
token: this.client.access_token,
|
||||||
|
targets: chat_ids,
|
||||||
|
})
|
||||||
|
if (re.code != 200) throw new CallbackError(re)
|
||||||
|
}
|
||||||
|
async removeFavouriteChats(chat_ids: string[]) {
|
||||||
|
try {
|
||||||
|
await this.removeFavouriteChatsOrThrow(chat_ids)
|
||||||
|
return true
|
||||||
|
} catch (_) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async removeFavouriteChatsOrThrow(chat_ids: string[]) {
|
||||||
|
const re = await this.client.invoke("User.removeContacts", {
|
||||||
|
token: this.client.access_token,
|
||||||
|
targets: chat_ids,
|
||||||
|
})
|
||||||
|
if (re.code != 200) throw new CallbackError(re)
|
||||||
|
}
|
||||||
|
async getMyFavouriteChatBeans() {
|
||||||
|
try {
|
||||||
|
return await this.getMyFavouriteChatBeansOrThrow()
|
||||||
|
} catch (_) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getMyFavouriteChatBeansOrThrow() {
|
||||||
|
const re = await this.client.invoke("User.getMyContacts", {
|
||||||
|
token: this.client.access_token
|
||||||
|
})
|
||||||
|
if (re.code == 200)
|
||||||
|
return (re.data!.favourite_chats || re.data!.contacts_list) as ChatBean[]
|
||||||
|
throw new CallbackError(re)
|
||||||
|
}
|
||||||
|
async getMyFavouriteChats() {
|
||||||
|
try {
|
||||||
|
return await this.getMyFavouriteChatsOrThrow()
|
||||||
|
} catch (_) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getMyFavouriteChatsOrThrow() {
|
||||||
|
return (await this.getMyFavouriteChatBeansOrThrow()).map((bean) => new Chat(this.client, bean))
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* ================================================
|
||||||
|
* 最近对话
|
||||||
|
* ================================================
|
||||||
|
*/
|
||||||
|
async getMyRecentChatBeans() {
|
||||||
|
try {
|
||||||
|
return await this.getMyRecentChatBeansOrThrow()
|
||||||
|
} catch (_) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getMyRecentChatBeansOrThrow() {
|
||||||
|
const re = await this.client.invoke("User.getMyRecentChats", {
|
||||||
|
token: this.client.access_token
|
||||||
|
})
|
||||||
|
if (re.code == 200)
|
||||||
|
return re.data!.recent_chats as RecentChatBean[]
|
||||||
|
throw new CallbackError(re)
|
||||||
|
}
|
||||||
|
async getMyRecentChats() {
|
||||||
|
try {
|
||||||
|
return await this.getMyRecentChatsOrThrow()
|
||||||
|
} catch (_) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getMyRecentChatsOrThrow() {
|
||||||
|
return (await this.getMyRecentChatBeansOrThrow()).map((bean) => new RecentChat(this.client, bean))
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* ================================================
|
||||||
|
* 所有对话
|
||||||
|
* ================================================
|
||||||
|
*/
|
||||||
|
async getMyAllChatBeans() {
|
||||||
|
try {
|
||||||
|
return await this.getMyAllChatBeansOrThrow()
|
||||||
|
} catch (_) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getMyAllChatBeansOrThrow() {
|
||||||
|
const re = await this.client.invoke("User.getMyAllChats", {
|
||||||
|
token: this.client.access_token
|
||||||
|
})
|
||||||
|
if (re.code == 200)
|
||||||
|
return re.data!.all_chats as ChatBean[]
|
||||||
|
throw new CallbackError(re)
|
||||||
|
}
|
||||||
|
async getMyAllChats() {
|
||||||
|
try {
|
||||||
|
return await this.getMyAllChatsOrThrow()
|
||||||
|
} catch (_) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getMyAllChatsOrThrow() {
|
||||||
|
return (await this.getMyAllChatBeansOrThrow()).map((bean) => new Chat(this.client, bean))
|
||||||
|
}
|
||||||
|
}
|
||||||
5
client-protocol/bean/BaseChatSettingsBean.ts
Normal file
5
client-protocol/bean/BaseChatSettingsBean.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
interface BaseChatSettings {
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BaseChatSettings
|
||||||
15
client-protocol/bean/ChatBean.ts
Normal file
15
client-protocol/bean/ChatBean.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import BaseChatSettingsBean from "./BaseChatSettingsBean.ts"
|
||||||
|
import ChatType from "../type/ChatType.ts"
|
||||||
|
|
||||||
|
export default class ChatBean {
|
||||||
|
declare type: ChatType
|
||||||
|
declare id: string
|
||||||
|
declare title: string
|
||||||
|
declare avatar_file_hash?: string
|
||||||
|
declare settings?: BaseChatSettingsBean
|
||||||
|
|
||||||
|
declare is_member: boolean
|
||||||
|
declare is_admin: boolean
|
||||||
|
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
14
client-protocol/bean/GroupSettingsBean.ts
Normal file
14
client-protocol/bean/GroupSettingsBean.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import BaseChatSettings from "./BaseChatSettingsBean.ts"
|
||||||
|
|
||||||
|
interface GroupSettingsBean extends BaseChatSettings {
|
||||||
|
allow_new_member_join?: boolean
|
||||||
|
allow_new_member_from_invitation?: boolean
|
||||||
|
new_member_join_method?: 'disabled' | 'allowed_by_admin' | 'answered_and_allowed_by_admin'
|
||||||
|
answered_and_allowed_by_admin_question?: string
|
||||||
|
|
||||||
|
// 下面两个比较特殊, 由服务端给予
|
||||||
|
group_title: string
|
||||||
|
group_name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GroupSettingsBean
|
||||||
8
client-protocol/bean/JoinRequestBean.ts
Normal file
8
client-protocol/bean/JoinRequestBean.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export default class JoinRequestBean {
|
||||||
|
declare user_id: string
|
||||||
|
declare nickname: string
|
||||||
|
declare avatar_file_hash?: string
|
||||||
|
declare reason?: string
|
||||||
|
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
7
client-protocol/bean/MessageBean.ts
Normal file
7
client-protocol/bean/MessageBean.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export default class MessageBean {
|
||||||
|
declare id: number
|
||||||
|
declare text: string
|
||||||
|
declare user_id?: string
|
||||||
|
declare chat_id?: string
|
||||||
|
declare time: string
|
||||||
|
}
|
||||||
5
client-protocol/bean/RecentChatBean.ts
Normal file
5
client-protocol/bean/RecentChatBean.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import ChatBean from "./ChatBean.ts"
|
||||||
|
|
||||||
|
export default class RecentChatBean extends ChatBean {
|
||||||
|
declare content: string
|
||||||
|
}
|
||||||
6
client-protocol/bean/UserBean.ts
Normal file
6
client-protocol/bean/UserBean.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export default class UserBean {
|
||||||
|
declare id: string
|
||||||
|
declare username?: string
|
||||||
|
declare nickname: string
|
||||||
|
declare avatar_file_hash?: string
|
||||||
|
}
|
||||||
28
client-protocol/main.ts
Normal file
28
client-protocol/main.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import Chat from "./Chat.ts"
|
||||||
|
import User from "./User.ts"
|
||||||
|
import UserMySelf from "./UserMySelf.ts"
|
||||||
|
import UserBean from "./bean/UserBean.ts"
|
||||||
|
import ChatBean from "./bean/ChatBean.ts"
|
||||||
|
import GroupSettingsBean from "./bean/GroupSettingsBean.ts"
|
||||||
|
import JoinRequestBean from "./bean/JoinRequestBean.ts"
|
||||||
|
import MessageBean from "./bean/MessageBean.ts"
|
||||||
|
import RecentChatBean from "./bean/RecentChatBean.ts"
|
||||||
|
|
||||||
|
import LingChairClient from "./LingChairClient.ts"
|
||||||
|
import CallbackError from "./CallbackError.ts"
|
||||||
|
|
||||||
|
export {
|
||||||
|
LingChairClient,
|
||||||
|
CallbackError,
|
||||||
|
|
||||||
|
Chat,
|
||||||
|
User,
|
||||||
|
UserMySelf,
|
||||||
|
|
||||||
|
UserBean,
|
||||||
|
ChatBean,
|
||||||
|
MessageBean,
|
||||||
|
RecentChatBean,
|
||||||
|
JoinRequestBean,
|
||||||
|
}
|
||||||
|
export type { GroupSettingsBean }
|
||||||
11
client-protocol/package.json
Normal file
11
client-protocol/package.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "lingchair-client-protocol",
|
||||||
|
"type": "module",
|
||||||
|
"main": "./main.ts",
|
||||||
|
"dependencies": {
|
||||||
|
"lingchair-internal-shared": "*",
|
||||||
|
"marked": "16.3.0",
|
||||||
|
"socket.io-client": "4.8.1",
|
||||||
|
"crypto-browserify": "3.12.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
3
client-protocol/type/ChatType.ts
Normal file
3
client-protocol/type/ChatType.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
type ChatType = 'private' | 'group'
|
||||||
|
|
||||||
|
export default ChatType
|
||||||
3
client-protocol/type/JoinRequestAction.ts
Normal file
3
client-protocol/type/JoinRequestAction.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
type JoinRequestAction = 'accept' | 'remove'
|
||||||
|
|
||||||
|
export default JoinRequestAction
|
||||||
8
client-protocol/type/OnMessageData.ts
Normal file
8
client-protocol/type/OnMessageData.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import MessageBean from '../bean/MessageBean.ts'
|
||||||
|
|
||||||
|
interface OnMessageData {
|
||||||
|
chat: string
|
||||||
|
msg: MessageBean
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OnMessageData
|
||||||
23
client/ClientCache.ts
Normal file
23
client/ClientCache.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { Chat, User } from "lingchair-client-protocol"
|
||||||
|
import getClient from "./getClient"
|
||||||
|
|
||||||
|
type CouldCached = User | Chat | null
|
||||||
|
export default class ClientCache {
|
||||||
|
static caches: { [key: string]: CouldCached } = {}
|
||||||
|
|
||||||
|
static async getUser(id: string) {
|
||||||
|
const k = 'user_' + id
|
||||||
|
if (this.caches[k] != null)
|
||||||
|
return this.caches[k] as User | null
|
||||||
|
this.caches[k] = await User.getById(getClient(), id)
|
||||||
|
return this.caches[k]
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getChat(id: string) {
|
||||||
|
const k = 'chat_' + id
|
||||||
|
if (this.caches[k] != null)
|
||||||
|
return this.caches[k] as Chat | null
|
||||||
|
this.caches[k] = await Chat.getById(getClient(), id)
|
||||||
|
return this.caches[k]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
import { CryptoES } from './Imports.ts'
|
|
||||||
|
|
||||||
const dataIsEmpty = !localStorage.tws_data || localStorage.tws_data == ''
|
|
||||||
|
|
||||||
const aes = {
|
|
||||||
enc: (m: string, k: string) => CryptoES.AES.encrypt(m, k).toString(CryptoES.Utf8),
|
|
||||||
dec: (m: string, k: string) => CryptoES.AES.decrypt(m, k).toString(CryptoES.Utf8),
|
|
||||||
}
|
|
||||||
|
|
||||||
const key = location.host + '_TWS_姐姐'
|
|
||||||
|
|
||||||
if (dataIsEmpty) localStorage.tws_data = aes.enc('{}', key)
|
|
||||||
|
|
||||||
let _dec = aes.dec(localStorage.tws_data, key)
|
|
||||||
if (_dec == '') _dec = '{}'
|
|
||||||
|
|
||||||
const _data_cached = JSON.parse(_dec)
|
|
||||||
|
|
||||||
// 類型定義
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
data: {
|
|
||||||
apply: () => void
|
|
||||||
access_token?: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// deno-lint-ignore no-window
|
|
||||||
(window.data == null) && (window.data = new Proxy({
|
|
||||||
apply() {
|
|
||||||
localStorage.tws_data = aes.enc(JSON.stringify(_data_cached), key)
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
get(_obj, k) {
|
|
||||||
return _data_cached[k]
|
|
||||||
},
|
|
||||||
set(_obj, k, v) {
|
|
||||||
_data_cached[k] = v
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
|
|
||||||
// deno-lint-ignore no-window
|
|
||||||
export default window.data
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
// @ts-types="./typedef/react@18.3.18.d.ts"
|
|
||||||
import * as React from './static/react-esm-18.3.1.static.js'
|
|
||||||
import * as ReactDOM from './static/react-dom-esm-18.3.1.static.js'
|
|
||||||
import * as CryptoES from './static/crypto-es-3.1.0.static.mjs'
|
|
||||||
|
|
||||||
import type { Dialog } from 'https://unpkg.com/mdui@2.1.4/components/dialog/index.d.ts'
|
|
||||||
import type { TextField } from 'https://unpkg.com/mdui@2.1.4/components/text-field/index.d.ts'
|
|
||||||
import type { Button } from 'https://unpkg.com/mdui@2.1.4/components/button/index.d.ts'
|
|
||||||
import type { NavigationRail } from 'https://unpkg.com/mdui@2.1.4/components/navigation-rail/navigation-rail.d.ts'
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
namespace React {
|
|
||||||
namespace JSX {
|
|
||||||
interface IntrinsicAttributes {
|
|
||||||
id?: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
React,
|
|
||||||
ReactDOM,
|
|
||||||
CryptoES,
|
|
||||||
|
|
||||||
Dialog as MduiDialog,
|
|
||||||
TextField as MduiTextField,
|
|
||||||
Button as MduiButton,
|
|
||||||
NavigationRail as MduiNavigationRail,
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
export type CallMethod =
|
|
||||||
"User.auth" |
|
|
||||||
"User.register" |
|
|
||||||
"User.login"
|
|
||||||
|
|
||||||
export type ClientEvent =
|
|
||||||
"Client.onMessage"
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import { io, Socket } from 'https://unpkg.com/socket.io-client@4.8.1/dist/socket.io.esm.min.js'
|
|
||||||
import { CallMethod, ClientEvent } from './ApiDeclare.ts'
|
|
||||||
import ApiCallbackMessage from './ApiCallbackMessage.ts'
|
|
||||||
|
|
||||||
type UnknownObject = { [key: string]: unknown }
|
|
||||||
|
|
||||||
class Client {
|
|
||||||
static socket?: Socket
|
|
||||||
static events: { [key: string]: (data: UnknownObject) => UnknownObject } = {}
|
|
||||||
static connect() {
|
|
||||||
this.socket?.disconnect()
|
|
||||||
this.socket && delete this.socket
|
|
||||||
this.socket = io()
|
|
||||||
this.socket!.on("The_White_Silk", (name: string, data: UnknownObject, callback: (ret: UnknownObject) => void) => {
|
|
||||||
try {
|
|
||||||
if (name == null || data == null) return
|
|
||||||
const re = this.events[name]?.(data)
|
|
||||||
re && callback(re)
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
static invoke(method: CallMethod, args: UnknownObject = {}, timeout: number = 5000): Promise<ApiCallbackMessage> {
|
|
||||||
if (this.socket == null) throw new Error("客戶端未與伺服器端建立連接!")
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.socket!.timeout(timeout).emit("The_White_Silk", method, args, (err: string, res: ApiCallbackMessage) => {
|
|
||||||
if (err) return reject(err)
|
|
||||||
resolve(res)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
static on(eventName: ClientEvent, func: (data: UnknownObject) => UnknownObject) {
|
|
||||||
this.events[eventName] = func
|
|
||||||
}
|
|
||||||
static off(eventName: ClientEvent){
|
|
||||||
delete this.events[eventName]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Client
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
export default class RecentChat {
|
|
||||||
declare id: string
|
|
||||||
declare title: string
|
|
||||||
declare avatar: string | null
|
|
||||||
declare content: string
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
export default class User {
|
|
||||||
declare id: string
|
|
||||||
declare count: number
|
|
||||||
declare username: string | null
|
|
||||||
declare nickname: string
|
|
||||||
declare avatar: string | null
|
|
||||||
}
|
|
||||||
71
client/data.ts
Normal file
71
client/data.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import crypto from 'node:crypto'
|
||||||
|
|
||||||
|
const dataIsEmpty = !localStorage.tws_data || localStorage.tws_data == ''
|
||||||
|
|
||||||
|
class Aes {
|
||||||
|
static randomIv() {
|
||||||
|
return crypto.randomBytes(12)
|
||||||
|
}
|
||||||
|
static normalizeKey(key: string, keyLength = 32) {
|
||||||
|
const hash = crypto.createHash('sha256')
|
||||||
|
hash.update(key)
|
||||||
|
const keyBuffer = hash.digest()
|
||||||
|
return keyLength ? keyBuffer.subarray(0, keyLength) : keyBuffer
|
||||||
|
}
|
||||||
|
static encrypt(data: string, key: string) {
|
||||||
|
const iv = this.randomIv()
|
||||||
|
return Buffer.concat([iv, crypto.createCipheriv("aes-256-gcm", this.normalizeKey(key), iv).update(data)]).toString('hex')
|
||||||
|
}
|
||||||
|
static decrypt(data: string, key: string) {
|
||||||
|
const buffer = Buffer.from(data, 'hex')
|
||||||
|
const iv = buffer.subarray(0, 12)
|
||||||
|
return crypto.createDecipheriv("aes-256-gcm", this.normalizeKey(key), iv).update(buffer.subarray(12)).toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尽可能防止被窃取, 虽然理论上还是会被窃取
|
||||||
|
const key = crypto.createHash('sha256').update(location.host + '_TWS_姐姐_' + navigator.userAgent).digest().toString('base64')
|
||||||
|
|
||||||
|
if (dataIsEmpty) localStorage.tws_data = Aes.encrypt('{}', key)
|
||||||
|
|
||||||
|
let _data_cached
|
||||||
|
try {
|
||||||
|
_data_cached = JSON.parse(Aes.decrypt(localStorage.tws_data, key))
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("数据解密失败, 使用空数据...", e)
|
||||||
|
_data_cached = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
type IData = {
|
||||||
|
refresh_token?: string
|
||||||
|
split_sizes: number[]
|
||||||
|
apply(): void
|
||||||
|
access_token?: string
|
||||||
|
device_id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
data?: IData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = new Proxy({} as IData, {
|
||||||
|
get(_obj, k) {
|
||||||
|
if (k == '_cached') return _data_cached
|
||||||
|
if (k == 'apply') return () => localStorage.tws_data = Aes.encrypt(JSON.stringify(_data_cached), key)
|
||||||
|
return _data_cached[k]
|
||||||
|
},
|
||||||
|
set(_obj, k, v) {
|
||||||
|
if (k == '_cached') return false
|
||||||
|
_data_cached[k] = v
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (new URL(location.href).searchParams.get('export_data') == 'true') {
|
||||||
|
window.data = data
|
||||||
|
console.warn("警告: 将 data 暴露到 window 有可能会导致令牌泄露!")
|
||||||
|
}
|
||||||
|
|
||||||
|
export default data
|
||||||
8
client/env.d.ts
vendored
Normal file
8
client/env.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/// <reference types="mdui/jsx.zh-cn.d.ts" />
|
||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
declare const __APP_VERSION__: string
|
||||||
|
declare const __GIT_HASH__: string
|
||||||
|
declare const __GIT_HASH_FULL__: string
|
||||||
|
declare const __GIT_BRANCH__: string
|
||||||
|
declare const __BUILD_TIME__: string
|
||||||
19
client/getClient.ts
Normal file
19
client/getClient.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { LingChairClient } from 'lingchair-client-protocol'
|
||||||
|
import data from "./data.ts"
|
||||||
|
import { UAParser } from 'ua-parser-js'
|
||||||
|
import { randomUUID } from 'lingchair-internal-shared'
|
||||||
|
import performAuth from './performAuth.ts'
|
||||||
|
|
||||||
|
if (!data.device_id) {
|
||||||
|
const ua = new UAParser(navigator.userAgent)
|
||||||
|
data.device_id = `LingChair_Web_${ua.getOS() || 'unknown-os'}-${ua.getDevice().type || 'unknown_device'}-${randomUUID()}`
|
||||||
|
}
|
||||||
|
const client = new LingChairClient({
|
||||||
|
server_url: '',
|
||||||
|
device_id: data.device_id,
|
||||||
|
auto_fresh_token: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default function getClient() {
|
||||||
|
return client
|
||||||
|
}
|
||||||
BIN
client/icon.ico
Normal file
BIN
client/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
@@ -7,124 +7,18 @@
|
|||||||
<meta name="renderer" content="webkit" />
|
<meta name="renderer" content="webkit" />
|
||||||
|
|
||||||
<!-- UI -->
|
<!-- UI -->
|
||||||
<script src="./static/mdui-2.1.4.global.min.js">
|
|
||||||
</script>
|
|
||||||
<link rel="icon" href="icon.ico" />
|
<link rel="icon" href="icon.ico" />
|
||||||
<link rel="stylesheet" href="./static/mdui-2.1.4.css" />
|
|
||||||
<link rel="stylesheet" href="./static/material_icons.css" />
|
<link rel="stylesheet" href="./static/material_icons.css" />
|
||||||
|
|
||||||
<script src="./static/jquery-3.7.1.min.js"></script>
|
<title>LingChair</title>
|
||||||
<script src="./static/splitjs-1.6.5.min.js"></script>
|
|
||||||
|
|
||||||
<title>TheWhiteSilk</title>
|
<link rel="stylesheet" href="./style.css" />
|
||||||
|
|
||||||
<style>
|
|
||||||
/* 滑条*/
|
|
||||||
.no-scroll-bar::-webkit-scrollbar {
|
|
||||||
width: 0px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* https://blog.csdn.net/qq_39347364/article/details/111996581*/
|
|
||||||
*::-webkit-scrollbar {
|
|
||||||
width: 7px;
|
|
||||||
height: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
*::-webkit-scrollbar-track {
|
|
||||||
width: 6px;
|
|
||||||
background: rgba(#101F1C, 0.1);
|
|
||||||
-webkit-border-radius: 2em;
|
|
||||||
-moz-border-radius: 2em;
|
|
||||||
border-radius: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
*::-webkit-scrollbar-thumb {
|
|
||||||
background-color: rgba(144, 147, 153, .5);
|
|
||||||
background-clip: padding-box;
|
|
||||||
min-height: 28px;
|
|
||||||
-webkit-border-radius: 2em;
|
|
||||||
-moz-border-radius: 2em;
|
|
||||||
border-radius: 2em;
|
|
||||||
transition: background-color .3s;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
*::-webkit-scrollbar-thumb:hover {
|
|
||||||
background-color: rgba(144, 147, 153, .3);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 使用系统字体 在部分系统表现很好*/
|
|
||||||
/* 我们至今仍未能知道桌面端浏览器字体的秘密*/
|
|
||||||
*:not(.material-icons, .mdui-icon, mdui-icon, .fa, .google-symbols) {
|
|
||||||
font-family: -apple-system, system-ui, -webkit-system-font !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
display: flex;
|
|
||||||
margin: 0 0 0 0;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
margin: 0 0 0 0;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 防止小尺寸图片模糊*/
|
|
||||||
* {
|
|
||||||
image-rendering: -moz-crisp-edges;
|
|
||||||
image-rendering: -o-crisp-edges;
|
|
||||||
image-rendering: -webkit-optimize-contrast;
|
|
||||||
image-rendering: crisp-edges;
|
|
||||||
-ms-interpolation-mode: nearest-neighbor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gutter {
|
|
||||||
background-color: rgb(var(--mdui-color-surface-variant));;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gutter.gutter-horizontal {
|
|
||||||
cursor: col-resize;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<mdui-snackbar close-on-outside-click id="public_snackbar"></mdui-snackbar>
|
|
||||||
|
|
||||||
<mdui-dialog close-on-overlay-click id="ErrorDialog">
|
<script type="module" src="./init.ts"></script>
|
||||||
<span slot="headline">错误</span>
|
|
||||||
<span slot="description" id="ErrorDialog_Message"></span>
|
|
||||||
</mdui-dialog>
|
|
||||||
|
|
||||||
<script nomodule>
|
|
||||||
alert('很抱歉, 此应用无法在较旧的浏览器运行, 请使用基于 Chromium 89+ 的浏览器(内核)使用 :(')
|
|
||||||
</script>
|
|
||||||
<script>
|
|
||||||
new URL(location.href).searchParams.get('debug') == 'true' && window.addEventListener('error', ({ message, filename, lineno, colno, error }) => {
|
|
||||||
const m = $("#ErrorDialog_Message")
|
|
||||||
const d = $("#ErrorDialog").get(0)
|
|
||||||
const s = d.open
|
|
||||||
d.open = true
|
|
||||||
m.html((s ? `${m.html()}<br/><br/>` : '') + `${message} (${filename || 'unknown'}:${lineno}:${colno})`)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<script type="module">
|
|
||||||
import App from './ui/App.tsx'
|
|
||||||
import { React, ReactDOM } from './Imports.ts'
|
|
||||||
ReactDOM.createRoot(document.getElementById('app')).render(React.createElement(App, null))
|
|
||||||
|
|
||||||
let onResize = () => {
|
|
||||||
document.body.style.setProperty('--whitesilk-widget-message-maxwidth', mdui.breakpoint().down('md') ? "80%" : "70%")
|
|
||||||
document.body.style.setProperty('--whitesilk-window-width', window.innerWidth + 'px')
|
|
||||||
document.body.style.setProperty('--whitesilk-window-height', window.innerHeight + 'px')
|
|
||||||
}
|
|
||||||
window.addEventListener('resize', onResize)
|
|
||||||
onResize()
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
41
client/init.ts
Normal file
41
client/init.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import 'mdui/mdui.css'
|
||||||
|
import 'mdui'
|
||||||
|
import { breakpoint } from "mdui"
|
||||||
|
|
||||||
|
import './env.d.ts'
|
||||||
|
|
||||||
|
import * as React from 'react'
|
||||||
|
import ReactDOM from 'react-dom/client'
|
||||||
|
|
||||||
|
import './ui/chat-elements/chat-image.ts'
|
||||||
|
import './ui/chat-elements/chat-video.ts'
|
||||||
|
import './ui/chat-elements/chat-file.ts'
|
||||||
|
import './ui/chat-elements/chat-text.ts'
|
||||||
|
import './ui/chat-elements/chat-mention.ts'
|
||||||
|
import './ui/chat-elements/chat-text-container.ts'
|
||||||
|
import './ui/chat-elements/chat-quote.ts'
|
||||||
|
import Main from "./ui/Main.tsx"
|
||||||
|
|
||||||
|
import performAuth from './performAuth.ts'
|
||||||
|
|
||||||
|
try {
|
||||||
|
await performAuth({})
|
||||||
|
} catch (e) {
|
||||||
|
console.log("验证失败", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById('app') as HTMLElement).render(React.createElement(Main))
|
||||||
|
|
||||||
|
const onResize = () => {
|
||||||
|
document.body.style.setProperty('--whitesilk-widget-message-maxwidth', breakpoint().down('md') ? "80%" : "70%")
|
||||||
|
// deno-lint-ignore no-window
|
||||||
|
document.body.style.setProperty('--whitesilk-window-width', window.innerWidth + 'px')
|
||||||
|
// deno-lint-ignore no-window
|
||||||
|
document.body.style.setProperty('--whitesilk-window-height', window.innerHeight + 'px')
|
||||||
|
}
|
||||||
|
// deno-lint-ignore no-window no-window-prefix
|
||||||
|
window.addEventListener('resize', onResize)
|
||||||
|
onResize()
|
||||||
|
|
||||||
|
const config = await fetch('config.json').then((re) => re.json())
|
||||||
|
config.title && (document.title = config.title)
|
||||||
32
client/package.json
Normal file
32
client/package.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "lingchair-client",
|
||||||
|
"type": "module",
|
||||||
|
"version": "0.1.0-alpha",
|
||||||
|
"scripts": {
|
||||||
|
"build": "npx vite build",
|
||||||
|
"build-watch": "npx vite --watch build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"dompurify": "3.2.7",
|
||||||
|
"lingchair-internal-shared": "*",
|
||||||
|
"marked": "16.3.0",
|
||||||
|
"mdui": "2.1.4",
|
||||||
|
"pinch-zoom-element": "1.1.1",
|
||||||
|
"react": "18.3.1",
|
||||||
|
"react-dom": "18.3.1",
|
||||||
|
"react-router": "7.10.1",
|
||||||
|
"socket.io-client": "4.8.1",
|
||||||
|
"split.js": "1.3.2",
|
||||||
|
"ua-parser-js": "2.0.6",
|
||||||
|
"use-context-selector": "2.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@rollup/wasm-node": "4.48.0",
|
||||||
|
"@types/react": "18.3.1",
|
||||||
|
"@types/react-dom": "18.3.1",
|
||||||
|
"@vitejs/plugin-react": "4.7.0",
|
||||||
|
"chalk": "5.4.1",
|
||||||
|
"vite": "7.2.6",
|
||||||
|
"vite-plugin-node-polyfills": "^0.24.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
31
client/performAuth.ts
Normal file
31
client/performAuth.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import data from "./data.ts"
|
||||||
|
import getClient from "./getClient.ts"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进行身份验证以接受客户端事件
|
||||||
|
*
|
||||||
|
* 使用验证方式优先级: 访问 > 刷新 > 账号密码
|
||||||
|
*
|
||||||
|
* 若传递了账号密码, 则同时缓存新的访问令牌和刷新令牌
|
||||||
|
*
|
||||||
|
* 如只传递两个令牌的其一, 按照优先级并在成功验证后赋值
|
||||||
|
*
|
||||||
|
* 多个验证方式不会逐一尝试
|
||||||
|
*/
|
||||||
|
export default async function performAuth(args: {
|
||||||
|
refresh_token?: string
|
||||||
|
account?: string
|
||||||
|
password?: string
|
||||||
|
}) {
|
||||||
|
if (args.account && args.password)
|
||||||
|
await getClient().authOrThrow({
|
||||||
|
account: args.account,
|
||||||
|
password: args.password,
|
||||||
|
})
|
||||||
|
else {
|
||||||
|
await getClient().authOrThrow({ refresh_token: args.refresh_token ? args.refresh_token : data.refresh_token, ignore_all_empty: true })
|
||||||
|
}
|
||||||
|
data.refresh_token = getClient().getCachedRefreshToken()
|
||||||
|
data.access_token = getClient().getCachedAccessToken()
|
||||||
|
data.apply()
|
||||||
|
}
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import { Base, BufferedBlockAlgorithm, HMAC, Hasher, Hex, Latin1, Utf8, WordArray } from "./crypto-es@3.1.0/core.mjs";
|
|
||||||
import { X64Word, X64WordArray } from "./crypto-es@3.1.0/x64-core.mjs";
|
|
||||||
import { Base64 } from "./crypto-es@3.1.0/enc-base64.mjs";
|
|
||||||
import { HmacMD5, MD5, MD5Algo } from "./crypto-es@3.1.0/md5.mjs";
|
|
||||||
import { EvpKDF, EvpKDFAlgo } from "./crypto-es@3.1.0/evpkdf.mjs";
|
|
||||||
import { BlockCipher, BlockCipherMode, CBC, Cipher, CipherParams, OpenSSLFormatter, OpenSSLKdf, PasswordBasedCipher, Pkcs7, SerializableCipher, StreamCipher } from "./crypto-es@3.1.0/cipher-core.mjs";
|
|
||||||
import { Utf16, Utf16BE, Utf16LE } from "./crypto-es@3.1.0/enc-utf16.mjs";
|
|
||||||
import { Base64url } from "./crypto-es@3.1.0/enc-base64url.mjs";
|
|
||||||
import { HmacSHA1, SHA1, SHA1Algo } from "./crypto-es@3.1.0/sha1.mjs";
|
|
||||||
import { HmacSHA256, SHA256, SHA256Algo } from "./crypto-es@3.1.0/sha256.mjs";
|
|
||||||
import { HmacSHA224, SHA224, SHA224Algo } from "./crypto-es@3.1.0/sha224.mjs";
|
|
||||||
import { HmacSHA512, SHA512, SHA512Algo } from "./crypto-es@3.1.0/sha512.mjs";
|
|
||||||
import { HmacSHA384, SHA384, SHA384Algo } from "./crypto-es@3.1.0/sha384.mjs";
|
|
||||||
import { HmacSHA3, SHA3, SHA3Algo } from "./crypto-es@3.1.0/sha3.mjs";
|
|
||||||
import { HmacRIPEMD160, RIPEMD160, RIPEMD160Algo } from "./crypto-es@3.1.0/ripemd160.mjs";
|
|
||||||
import { PBKDF2, PBKDF2Algo } from "./crypto-es@3.1.0/pbkdf2.mjs";
|
|
||||||
import { AES, AESAlgo } from "./crypto-es@3.1.0/aes.mjs";
|
|
||||||
import { DES, DESAlgo, TripleDES, TripleDESAlgo } from "./crypto-es@3.1.0/tripledes.mjs";
|
|
||||||
import { Rabbit, RabbitAlgo } from "./crypto-es@3.1.0/rabbit.mjs";
|
|
||||||
import { RabbitLegacy, RabbitLegacyAlgo } from "./crypto-es@3.1.0/rabbit-legacy.mjs";
|
|
||||||
import { RC4, RC4Algo, RC4Drop, RC4DropAlgo } from "./crypto-es@3.1.0/rc4.mjs";
|
|
||||||
import { Blowfish, BlowfishAlgo } from "./crypto-es@3.1.0/blowfish.mjs";
|
|
||||||
import { CFB } from "./crypto-es@3.1.0/mode-cfb.mjs";
|
|
||||||
import { CTR } from "./crypto-es@3.1.0/mode-ctr.mjs";
|
|
||||||
import { CTRGladman } from "./crypto-es@3.1.0/mode-ctr-gladman.mjs";
|
|
||||||
import { ECB } from "./crypto-es@3.1.0/mode-ecb.mjs";
|
|
||||||
import { OFB } from "./crypto-es@3.1.0/mode-ofb.mjs";
|
|
||||||
import { AnsiX923 } from "./crypto-es@3.1.0/pad-ansix923.mjs";
|
|
||||||
import { Iso10126 } from "./crypto-es@3.1.0/pad-iso10126.mjs";
|
|
||||||
import { ZeroPadding } from "./crypto-es@3.1.0/pad-zeropadding.mjs";
|
|
||||||
import { Iso97971 } from "./crypto-es@3.1.0/pad-iso97971.mjs";
|
|
||||||
import { NoPadding } from "./crypto-es@3.1.0/pad-nopadding.mjs";
|
|
||||||
import { HexFormatter } from "./crypto-es@3.1.0/format-hex.mjs";
|
|
||||||
|
|
||||||
export { AES, AESAlgo, AnsiX923, Base, Base64, Base64url, BlockCipher, BlockCipherMode, Blowfish, BlowfishAlgo, BufferedBlockAlgorithm, CBC, CFB, CTR, CTRGladman, Cipher, CipherParams, DES, DESAlgo, ECB, EvpKDF, EvpKDFAlgo, HMAC, Hasher, Hex, HexFormatter, HmacMD5, HmacRIPEMD160, HmacSHA1, HmacSHA224, HmacSHA256, HmacSHA3, HmacSHA384, HmacSHA512, Iso10126, Iso97971, Latin1, MD5, MD5Algo, NoPadding, OFB, OpenSSLFormatter, OpenSSLKdf, PBKDF2, PBKDF2Algo, PasswordBasedCipher, Pkcs7, RC4, RC4Algo, RC4Drop, RC4DropAlgo, RIPEMD160, RIPEMD160Algo, Rabbit, RabbitAlgo, RabbitLegacy, RabbitLegacyAlgo, SHA1, SHA1Algo, SHA224, SHA224Algo, SHA256, SHA256Algo, SHA3, SHA384, SHA384Algo, SHA3Algo, SHA512, SHA512Algo, SerializableCipher, StreamCipher, TripleDES, TripleDESAlgo, Utf16, Utf16BE, Utf16LE, Utf8, WordArray, X64Word, X64WordArray, ZeroPadding };
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
import { BlockCipher } from "./cipher-core.mjs";
|
|
||||||
|
|
||||||
//#region src/aes.ts
|
|
||||||
const _SBOX = [];
|
|
||||||
const INV_SBOX = [];
|
|
||||||
const _SUB_MIX_0 = [];
|
|
||||||
const _SUB_MIX_1 = [];
|
|
||||||
const _SUB_MIX_2 = [];
|
|
||||||
const _SUB_MIX_3 = [];
|
|
||||||
const INV_SUB_MIX_0 = [];
|
|
||||||
const INV_SUB_MIX_1 = [];
|
|
||||||
const INV_SUB_MIX_2 = [];
|
|
||||||
const INV_SUB_MIX_3 = [];
|
|
||||||
const RCON = [
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
4,
|
|
||||||
8,
|
|
||||||
16,
|
|
||||||
32,
|
|
||||||
64,
|
|
||||||
128,
|
|
||||||
27,
|
|
||||||
54
|
|
||||||
];
|
|
||||||
/**
|
|
||||||
* AES block cipher algorithm.
|
|
||||||
*/
|
|
||||||
var AESAlgo = class extends BlockCipher {
|
|
||||||
/** Number of rounds for this key size */
|
|
||||||
_nRounds;
|
|
||||||
/** Previous key for optimization */
|
|
||||||
_keyPriorReset;
|
|
||||||
/** Key schedule for encryption */
|
|
||||||
_keySchedule;
|
|
||||||
/** Inverse key schedule for decryption */
|
|
||||||
_invKeySchedule;
|
|
||||||
/** Key size in 32-bit words */
|
|
||||||
static keySize = 256 / 32;
|
|
||||||
_doReset() {
|
|
||||||
let t;
|
|
||||||
if (this._nRounds && this._keyPriorReset === this._key) return;
|
|
||||||
this._keyPriorReset = this._key;
|
|
||||||
const key = this._keyPriorReset;
|
|
||||||
const keyWords = key.words;
|
|
||||||
const keySize = key.sigBytes / 4;
|
|
||||||
this._nRounds = keySize + 6;
|
|
||||||
const nRounds = this._nRounds;
|
|
||||||
const ksRows = (nRounds + 1) * 4;
|
|
||||||
this._keySchedule = [];
|
|
||||||
const keySchedule = this._keySchedule;
|
|
||||||
for (let ksRow = 0; ksRow < ksRows; ksRow += 1) if (ksRow < keySize) keySchedule[ksRow] = keyWords[ksRow];
|
|
||||||
else {
|
|
||||||
t = keySchedule[ksRow - 1];
|
|
||||||
if (!(ksRow % keySize)) {
|
|
||||||
t = t << 8 | t >>> 24;
|
|
||||||
t = _SBOX[t >>> 24] << 24 | _SBOX[t >>> 16 & 255] << 16 | _SBOX[t >>> 8 & 255] << 8 | _SBOX[t & 255];
|
|
||||||
t ^= RCON[ksRow / keySize | 0] << 24;
|
|
||||||
} else if (keySize > 6 && ksRow % keySize === 4) t = _SBOX[t >>> 24] << 24 | _SBOX[t >>> 16 & 255] << 16 | _SBOX[t >>> 8 & 255] << 8 | _SBOX[t & 255];
|
|
||||||
keySchedule[ksRow] = keySchedule[ksRow - keySize] ^ t;
|
|
||||||
}
|
|
||||||
this._invKeySchedule = [];
|
|
||||||
const invKeySchedule = this._invKeySchedule;
|
|
||||||
for (let invKsRow = 0; invKsRow < ksRows; invKsRow += 1) {
|
|
||||||
const ksRow = ksRows - invKsRow;
|
|
||||||
if (invKsRow % 4) t = keySchedule[ksRow];
|
|
||||||
else t = keySchedule[ksRow - 4];
|
|
||||||
if (invKsRow < 4 || ksRow <= 4) invKeySchedule[invKsRow] = t;
|
|
||||||
else invKeySchedule[invKsRow] = INV_SUB_MIX_0[_SBOX[t >>> 24]] ^ INV_SUB_MIX_1[_SBOX[t >>> 16 & 255]] ^ INV_SUB_MIX_2[_SBOX[t >>> 8 & 255]] ^ INV_SUB_MIX_3[_SBOX[t & 255]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
encryptBlock(M, offset) {
|
|
||||||
this._doCryptBlock(M, offset, this._keySchedule, _SUB_MIX_0, _SUB_MIX_1, _SUB_MIX_2, _SUB_MIX_3, _SBOX);
|
|
||||||
}
|
|
||||||
decryptBlock(M, offset) {
|
|
||||||
const _M = M;
|
|
||||||
let t = _M[offset + 1];
|
|
||||||
_M[offset + 1] = _M[offset + 3];
|
|
||||||
_M[offset + 3] = t;
|
|
||||||
this._doCryptBlock(_M, offset, this._invKeySchedule, INV_SUB_MIX_0, INV_SUB_MIX_1, INV_SUB_MIX_2, INV_SUB_MIX_3, INV_SBOX);
|
|
||||||
t = _M[offset + 1];
|
|
||||||
_M[offset + 1] = _M[offset + 3];
|
|
||||||
_M[offset + 3] = t;
|
|
||||||
}
|
|
||||||
_doCryptBlock(M, offset, keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX) {
|
|
||||||
const _M = M;
|
|
||||||
const nRounds = this._nRounds;
|
|
||||||
let s0 = _M[offset] ^ keySchedule[0];
|
|
||||||
let s1 = _M[offset + 1] ^ keySchedule[1];
|
|
||||||
let s2 = _M[offset + 2] ^ keySchedule[2];
|
|
||||||
let s3 = _M[offset + 3] ^ keySchedule[3];
|
|
||||||
let ksRow = 4;
|
|
||||||
for (let round = 1; round < nRounds; round += 1) {
|
|
||||||
const t0$1 = SUB_MIX_0[s0 >>> 24] ^ SUB_MIX_1[s1 >>> 16 & 255] ^ SUB_MIX_2[s2 >>> 8 & 255] ^ SUB_MIX_3[s3 & 255] ^ keySchedule[ksRow];
|
|
||||||
ksRow += 1;
|
|
||||||
const t1$1 = SUB_MIX_0[s1 >>> 24] ^ SUB_MIX_1[s2 >>> 16 & 255] ^ SUB_MIX_2[s3 >>> 8 & 255] ^ SUB_MIX_3[s0 & 255] ^ keySchedule[ksRow];
|
|
||||||
ksRow += 1;
|
|
||||||
const t2$1 = SUB_MIX_0[s2 >>> 24] ^ SUB_MIX_1[s3 >>> 16 & 255] ^ SUB_MIX_2[s0 >>> 8 & 255] ^ SUB_MIX_3[s1 & 255] ^ keySchedule[ksRow];
|
|
||||||
ksRow += 1;
|
|
||||||
const t3$1 = SUB_MIX_0[s3 >>> 24] ^ SUB_MIX_1[s0 >>> 16 & 255] ^ SUB_MIX_2[s1 >>> 8 & 255] ^ SUB_MIX_3[s2 & 255] ^ keySchedule[ksRow];
|
|
||||||
ksRow += 1;
|
|
||||||
s0 = t0$1;
|
|
||||||
s1 = t1$1;
|
|
||||||
s2 = t2$1;
|
|
||||||
s3 = t3$1;
|
|
||||||
}
|
|
||||||
const t0 = (SBOX[s0 >>> 24] << 24 | SBOX[s1 >>> 16 & 255] << 16 | SBOX[s2 >>> 8 & 255] << 8 | SBOX[s3 & 255]) ^ keySchedule[ksRow];
|
|
||||||
ksRow += 1;
|
|
||||||
const t1 = (SBOX[s1 >>> 24] << 24 | SBOX[s2 >>> 16 & 255] << 16 | SBOX[s3 >>> 8 & 255] << 8 | SBOX[s0 & 255]) ^ keySchedule[ksRow];
|
|
||||||
ksRow += 1;
|
|
||||||
const t2 = (SBOX[s2 >>> 24] << 24 | SBOX[s3 >>> 16 & 255] << 16 | SBOX[s0 >>> 8 & 255] << 8 | SBOX[s1 & 255]) ^ keySchedule[ksRow];
|
|
||||||
ksRow += 1;
|
|
||||||
const t3 = (SBOX[s3 >>> 24] << 24 | SBOX[s0 >>> 16 & 255] << 16 | SBOX[s1 >>> 8 & 255] << 8 | SBOX[s2 & 255]) ^ keySchedule[ksRow];
|
|
||||||
ksRow += 1;
|
|
||||||
_M[offset] = t0;
|
|
||||||
_M[offset + 1] = t1;
|
|
||||||
_M[offset + 2] = t2;
|
|
||||||
_M[offset + 3] = t3;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Shortcut functions to the cipher's object interface.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* var ciphertext = AES.encrypt(message, key, cfg);
|
|
||||||
* var plaintext = AES.decrypt(ciphertext, key, cfg);
|
|
||||||
*/
|
|
||||||
const AES = BlockCipher._createHelper(AESAlgo);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { AES, AESAlgo };
|
|
||||||
//# sourceMappingURL=aes.mjs.map
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,636 +0,0 @@
|
|||||||
import { Base, BufferedBlockAlgorithm, Hex, WordArray } from "./core.mjs";
|
|
||||||
import { Base64 } from "./enc-base64.mjs";
|
|
||||||
import { EvpKDFAlgo } from "./evpkdf.mjs";
|
|
||||||
|
|
||||||
//#region src/cipher-core.ts
|
|
||||||
/**
|
|
||||||
* Abstract base cipher template.
|
|
||||||
* Provides the foundation for all encryption and decryption algorithms.
|
|
||||||
*
|
|
||||||
* @property keySize - This cipher's key size in words (default: 4 = 128 bits)
|
|
||||||
* @property ivSize - This cipher's IV size in words (default: 4 = 128 bits)
|
|
||||||
* @property _ENC_XFORM_MODE - A constant representing encryption mode
|
|
||||||
* @property _DEC_XFORM_MODE - A constant representing decryption mode
|
|
||||||
*/
|
|
||||||
var Cipher = class Cipher extends BufferedBlockAlgorithm {
|
|
||||||
/** Encryption mode constant */
|
|
||||||
static _ENC_XFORM_MODE = 1;
|
|
||||||
/** Decryption mode constant */
|
|
||||||
static _DEC_XFORM_MODE = 2;
|
|
||||||
/** Default key size in words (128 bits) */
|
|
||||||
static keySize = 128 / 32;
|
|
||||||
/** Default IV size in words (128 bits) */
|
|
||||||
static ivSize = 128 / 32;
|
|
||||||
/** Configuration options */
|
|
||||||
cfg;
|
|
||||||
/** Transform mode (encryption or decryption) */
|
|
||||||
_xformMode;
|
|
||||||
/** The key */
|
|
||||||
_key;
|
|
||||||
/** Block size in words */
|
|
||||||
blockSize = 128 / 32;
|
|
||||||
/**
|
|
||||||
* Initializes a newly created cipher.
|
|
||||||
*
|
|
||||||
* @param xformMode - Either the encryption or decryption transformation mode constant
|
|
||||||
* @param key - The key
|
|
||||||
* @param cfg - Configuration options to use for this operation
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const cipher = new AESAlgo(
|
|
||||||
* Cipher._ENC_XFORM_MODE, keyWordArray, { iv: ivWordArray }
|
|
||||||
* );
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
constructor(xformMode, key, cfg) {
|
|
||||||
super();
|
|
||||||
this.cfg = Object.assign({}, cfg);
|
|
||||||
this._xformMode = xformMode;
|
|
||||||
this._key = key;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Creates this cipher in encryption mode.
|
|
||||||
*
|
|
||||||
* @param key - The key
|
|
||||||
* @param cfg - Configuration options to use for this operation
|
|
||||||
* @returns A cipher instance
|
|
||||||
* @static
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const cipher = AESAlgo.createEncryptor(keyWordArray, { iv: ivWordArray });
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
static createEncryptor(key, cfg) {
|
|
||||||
return this.create(Cipher._ENC_XFORM_MODE, key, cfg);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Creates this cipher in decryption mode.
|
|
||||||
*
|
|
||||||
* @param key - The key
|
|
||||||
* @param cfg - Configuration options to use for this operation
|
|
||||||
* @returns A cipher instance
|
|
||||||
* @static
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const cipher = AESAlgo.createDecryptor(keyWordArray, { iv: ivWordArray });
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
static createDecryptor(key, cfg) {
|
|
||||||
return this.create(Cipher._DEC_XFORM_MODE, key, cfg);
|
|
||||||
}
|
|
||||||
static create(...args) {
|
|
||||||
if (args.length >= 2 && typeof args[0] === "number") {
|
|
||||||
const [xformMode, key, cfg] = args;
|
|
||||||
const instance = new this(xformMode, key, cfg);
|
|
||||||
instance.reset();
|
|
||||||
return instance;
|
|
||||||
} else return new this(...args);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Creates shortcut functions to a cipher's object interface.
|
|
||||||
*
|
|
||||||
* @param SubCipher - The cipher to create a helper for
|
|
||||||
* @returns An object with encrypt and decrypt shortcut functions
|
|
||||||
* @static
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const AES = Cipher._createHelper(AESAlgo);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
static _createHelper(SubCipher) {
|
|
||||||
const selectCipherStrategy = (key) => {
|
|
||||||
if (typeof key === "string") return PasswordBasedCipher;
|
|
||||||
return SerializableCipher;
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
encrypt(message, key, cfg) {
|
|
||||||
return selectCipherStrategy(key).encrypt(SubCipher, message, key, cfg);
|
|
||||||
},
|
|
||||||
decrypt(ciphertext, key, cfg) {
|
|
||||||
return selectCipherStrategy(key).decrypt(SubCipher, ciphertext, key, cfg);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Resets this cipher to its initial state.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* cipher.reset();
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
reset() {
|
|
||||||
super.reset();
|
|
||||||
this._doReset();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Adds data to be encrypted or decrypted.
|
|
||||||
*
|
|
||||||
* @param dataUpdate - The data to encrypt or decrypt
|
|
||||||
* @returns The data after processing
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const encrypted = cipher.process('data');
|
|
||||||
* const encrypted = cipher.process(wordArray);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
process(dataUpdate) {
|
|
||||||
this._append(dataUpdate);
|
|
||||||
return this._process();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Finalizes the encryption or decryption process.
|
|
||||||
* Note that the finalize operation is effectively a destructive, read-once operation.
|
|
||||||
*
|
|
||||||
* @param dataUpdate - The final data to encrypt or decrypt
|
|
||||||
* @returns The data after final processing
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const encrypted = cipher.finalize();
|
|
||||||
* const encrypted = cipher.finalize('data');
|
|
||||||
* const encrypted = cipher.finalize(wordArray);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
finalize(dataUpdate) {
|
|
||||||
if (dataUpdate) this._append(dataUpdate);
|
|
||||||
const finalProcessedData = this._doFinalize();
|
|
||||||
return finalProcessedData;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Abstract base stream cipher template.
|
|
||||||
* Stream ciphers process data one unit at a time rather than in blocks.
|
|
||||||
*
|
|
||||||
* @property blockSize - The number of 32-bit words this cipher operates on (default: 1 = 32 bits)
|
|
||||||
*/
|
|
||||||
var StreamCipher = class extends Cipher {
|
|
||||||
blockSize = 1;
|
|
||||||
constructor(xformMode, key, cfg) {
|
|
||||||
super(xformMode, key, cfg);
|
|
||||||
this.blockSize = 1;
|
|
||||||
}
|
|
||||||
_doFinalize() {
|
|
||||||
const finalProcessedBlocks = this._process(true);
|
|
||||||
return finalProcessedBlocks;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Abstract base block cipher mode template.
|
|
||||||
* Defines how multiple blocks are processed together.
|
|
||||||
*/
|
|
||||||
var BlockCipherMode = class extends Base {
|
|
||||||
/** The cipher instance */
|
|
||||||
_cipher;
|
|
||||||
/** The initialization vector */
|
|
||||||
_iv;
|
|
||||||
/** The previous block (for chaining modes) */
|
|
||||||
_prevBlock;
|
|
||||||
/**
|
|
||||||
* Initializes a newly created mode.
|
|
||||||
*
|
|
||||||
* @param cipher - A block cipher instance
|
|
||||||
* @param iv - The IV words
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const mode = new CBCMode(cipher, iv.words);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
constructor(cipher, iv) {
|
|
||||||
super();
|
|
||||||
this._cipher = cipher;
|
|
||||||
this._iv = iv;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Creates this mode for encryption.
|
|
||||||
*
|
|
||||||
* @param cipher - A block cipher instance
|
|
||||||
* @param iv - The IV words
|
|
||||||
* @returns The mode instance
|
|
||||||
* @static
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const mode = CBC.createEncryptor(cipher, iv.words);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
static createEncryptor(cipher, iv) {
|
|
||||||
return this.Encryptor.create(cipher, iv);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Creates this mode for decryption.
|
|
||||||
*
|
|
||||||
* @param cipher - A block cipher instance
|
|
||||||
* @param iv - The IV words
|
|
||||||
* @returns The mode instance
|
|
||||||
* @static
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const mode = CBC.createDecryptor(cipher, iv.words);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
static createDecryptor(cipher, iv) {
|
|
||||||
return this.Decryptor.create(cipher, iv);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Process a block of data
|
|
||||||
* Must be implemented by concrete modes
|
|
||||||
*/
|
|
||||||
processBlock(_words, _offset) {}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* XOR blocks for cipher block chaining
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
function xorBlock(words, offset, blockSize) {
|
|
||||||
const _words = words;
|
|
||||||
let block;
|
|
||||||
const iv = this._iv;
|
|
||||||
if (iv) {
|
|
||||||
block = iv;
|
|
||||||
this._iv = void 0;
|
|
||||||
} else block = this._prevBlock;
|
|
||||||
if (block) for (let i = 0; i < blockSize; i += 1) _words[offset + i] ^= block[i];
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* CBC Encryptor
|
|
||||||
*/
|
|
||||||
var CBCEncryptor = class extends BlockCipherMode {
|
|
||||||
/**
|
|
||||||
* Processes the data block at offset.
|
|
||||||
*
|
|
||||||
* @param words - The data words to operate on
|
|
||||||
* @param offset - The offset where the block starts
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* mode.processBlock(data.words, offset);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
processBlock(words, offset) {
|
|
||||||
const cipher = this._cipher;
|
|
||||||
const blockSize = cipher.blockSize;
|
|
||||||
xorBlock.call(this, words, offset, blockSize);
|
|
||||||
cipher.encryptBlock(words, offset);
|
|
||||||
this._prevBlock = words.slice(offset, offset + blockSize);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* CBC Decryptor
|
|
||||||
*/
|
|
||||||
var CBCDecryptor = class extends BlockCipherMode {
|
|
||||||
/**
|
|
||||||
* Processes the data block at offset.
|
|
||||||
*
|
|
||||||
* @param words - The data words to operate on
|
|
||||||
* @param offset - The offset where the block starts
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* mode.processBlock(data.words, offset);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
processBlock(words, offset) {
|
|
||||||
const cipher = this._cipher;
|
|
||||||
const blockSize = cipher.blockSize;
|
|
||||||
const thisBlock = words.slice(offset, offset + blockSize);
|
|
||||||
cipher.decryptBlock(words, offset);
|
|
||||||
xorBlock.call(this, words, offset, blockSize);
|
|
||||||
this._prevBlock = thisBlock;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Cipher Block Chaining mode.
|
|
||||||
* Each block is XORed with the previous ciphertext block before encryption.
|
|
||||||
*/
|
|
||||||
var CBC = class extends BlockCipherMode {
|
|
||||||
/** CBC Encryptor */
|
|
||||||
static Encryptor = CBCEncryptor;
|
|
||||||
/** CBC Decryptor */
|
|
||||||
static Decryptor = CBCDecryptor;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* PKCS #5/7 padding strategy.
|
|
||||||
* Pads data with bytes all of the same value as the count of padding bytes.
|
|
||||||
*/
|
|
||||||
const Pkcs7 = {
|
|
||||||
pad(data, blockSize) {
|
|
||||||
const blockSizeBytes = blockSize * 4;
|
|
||||||
const nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes;
|
|
||||||
const paddingWord = nPaddingBytes << 24 | nPaddingBytes << 16 | nPaddingBytes << 8 | nPaddingBytes;
|
|
||||||
const paddingWords = [];
|
|
||||||
for (let i = 0; i < nPaddingBytes; i += 4) paddingWords.push(paddingWord);
|
|
||||||
const padding = WordArray.create(paddingWords, nPaddingBytes);
|
|
||||||
data.concat(padding);
|
|
||||||
},
|
|
||||||
unpad(data) {
|
|
||||||
const nPaddingBytes = data.words[data.sigBytes - 1 >>> 2] & 255;
|
|
||||||
data.sigBytes -= nPaddingBytes;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Abstract base block cipher template.
|
|
||||||
* Block ciphers process data in fixed-size blocks.
|
|
||||||
*
|
|
||||||
* @property blockSize - The number of 32-bit words this cipher operates on (default: 4 = 128 bits)
|
|
||||||
*/
|
|
||||||
var BlockCipher = class extends Cipher {
|
|
||||||
/** Block mode instance */
|
|
||||||
_mode;
|
|
||||||
/**
|
|
||||||
* Initializes a newly created block cipher.
|
|
||||||
*
|
|
||||||
* @param xformMode - Transform mode
|
|
||||||
* @param key - The key
|
|
||||||
* @param cfg - Configuration options
|
|
||||||
*/
|
|
||||||
constructor(xformMode, key, cfg) {
|
|
||||||
super(xformMode, key, Object.assign({
|
|
||||||
mode: CBC,
|
|
||||||
padding: Pkcs7
|
|
||||||
}, cfg));
|
|
||||||
this.blockSize = 128 / 32;
|
|
||||||
}
|
|
||||||
reset() {
|
|
||||||
super.reset();
|
|
||||||
const { cfg } = this;
|
|
||||||
const { iv, mode } = cfg;
|
|
||||||
let modeCreator;
|
|
||||||
if (this._xformMode === this.constructor._ENC_XFORM_MODE) modeCreator = mode?.createEncryptor;
|
|
||||||
else {
|
|
||||||
modeCreator = mode?.createDecryptor;
|
|
||||||
this._minBufferSize = 1;
|
|
||||||
}
|
|
||||||
if (modeCreator && mode) {
|
|
||||||
this._mode = modeCreator.call(mode, this, iv?.words);
|
|
||||||
this._mode.__creator = modeCreator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_doProcessBlock(words, offset) {
|
|
||||||
this._mode?.processBlock(words, offset);
|
|
||||||
}
|
|
||||||
_doFinalize() {
|
|
||||||
let finalProcessedBlocks;
|
|
||||||
const { padding } = this.cfg;
|
|
||||||
if (this._xformMode === this.constructor._ENC_XFORM_MODE) {
|
|
||||||
if (padding) padding.pad(this._data, this.blockSize);
|
|
||||||
finalProcessedBlocks = this._process(true);
|
|
||||||
} else {
|
|
||||||
finalProcessedBlocks = this._process(true);
|
|
||||||
if (padding) padding.unpad(finalProcessedBlocks);
|
|
||||||
}
|
|
||||||
return finalProcessedBlocks;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* A collection of cipher parameters.
|
|
||||||
* Encapsulates all the parameters used in a cipher operation.
|
|
||||||
*
|
|
||||||
* @property ciphertext - The raw ciphertext
|
|
||||||
* @property key - The key to this ciphertext
|
|
||||||
* @property iv - The IV used in the ciphering operation
|
|
||||||
* @property salt - The salt used with a key derivation function
|
|
||||||
* @property algorithm - The cipher algorithm
|
|
||||||
* @property mode - The block mode used in the ciphering operation
|
|
||||||
* @property padding - The padding scheme used in the ciphering operation
|
|
||||||
* @property blockSize - The block size of the cipher
|
|
||||||
* @property formatter - The default formatting strategy
|
|
||||||
*/
|
|
||||||
var CipherParams = class CipherParams extends Base {
|
|
||||||
ciphertext;
|
|
||||||
key;
|
|
||||||
iv;
|
|
||||||
salt;
|
|
||||||
algorithm;
|
|
||||||
mode;
|
|
||||||
padding;
|
|
||||||
blockSize;
|
|
||||||
formatter;
|
|
||||||
/**
|
|
||||||
* Initializes a newly created cipher params object.
|
|
||||||
*
|
|
||||||
* @param cipherParams - An object with any of the possible cipher parameters
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const cipherParams = new CipherParams({
|
|
||||||
* ciphertext: ciphertextWordArray,
|
|
||||||
* key: keyWordArray,
|
|
||||||
* iv: ivWordArray,
|
|
||||||
* salt: saltWordArray,
|
|
||||||
* algorithm: AESAlgo,
|
|
||||||
* mode: CBC,
|
|
||||||
* padding: Pkcs7,
|
|
||||||
* blockSize: 4,
|
|
||||||
* formatter: OpenSSLFormatter
|
|
||||||
* });
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
constructor(cipherParams) {
|
|
||||||
super();
|
|
||||||
if (cipherParams) this.mixIn(cipherParams);
|
|
||||||
if (!this.formatter) this.formatter = OpenSSLFormatter;
|
|
||||||
}
|
|
||||||
static create(...args) {
|
|
||||||
const [cipherParams] = args;
|
|
||||||
return new CipherParams(cipherParams);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Converts this cipher params object to a string.
|
|
||||||
*
|
|
||||||
* @param formatter - The formatting strategy to use
|
|
||||||
* @returns The stringified cipher params
|
|
||||||
* @throws Error if neither the formatter nor the default formatter is set
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const string = cipherParams.toString();
|
|
||||||
* const string = cipherParams.toString(OpenSSLFormatter);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
toString(formatter) {
|
|
||||||
const fmt = formatter || this.formatter;
|
|
||||||
if (!fmt) throw new Error("cipher params formatter required");
|
|
||||||
return fmt.stringify(this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* OpenSSL formatting strategy.
|
|
||||||
* Formats cipher params in OpenSSL-compatible format.
|
|
||||||
*/
|
|
||||||
const OpenSSLFormatter = {
|
|
||||||
stringify(cipherParams) {
|
|
||||||
let wordArray;
|
|
||||||
const { ciphertext, salt } = cipherParams;
|
|
||||||
if (salt && ciphertext) wordArray = WordArray.create([1398893684, 1701076831]).concat(salt).concat(ciphertext);
|
|
||||||
else if (ciphertext) wordArray = ciphertext;
|
|
||||||
else wordArray = new WordArray();
|
|
||||||
return wordArray.toString(Base64);
|
|
||||||
},
|
|
||||||
parse(openSSLStr) {
|
|
||||||
let salt;
|
|
||||||
const ciphertext = Base64.parse(openSSLStr);
|
|
||||||
const ciphertextWords = ciphertext.words;
|
|
||||||
if (ciphertextWords[0] === 1398893684 && ciphertextWords[1] === 1701076831) {
|
|
||||||
salt = WordArray.create(ciphertextWords.slice(2, 4));
|
|
||||||
ciphertextWords.splice(0, 4);
|
|
||||||
ciphertext.sigBytes -= 16;
|
|
||||||
}
|
|
||||||
return CipherParams.create({
|
|
||||||
ciphertext,
|
|
||||||
salt
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* A cipher wrapper that returns ciphertext as a serializable cipher params object.
|
|
||||||
* Handles the serialization and deserialization of cipher operations.
|
|
||||||
*/
|
|
||||||
var SerializableCipher = class extends Base {
|
|
||||||
/** Configuration options */
|
|
||||||
static cfg = { format: OpenSSLFormatter };
|
|
||||||
/**
|
|
||||||
* Encrypts a message.
|
|
||||||
*
|
|
||||||
* @param cipher - The cipher algorithm to use
|
|
||||||
* @param message - The message to encrypt
|
|
||||||
* @param key - The key
|
|
||||||
* @param cfg - Configuration options to use for this operation
|
|
||||||
* @returns A cipher params object
|
|
||||||
* @static
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const ciphertextParams = SerializableCipher.encrypt(AESAlgo, message, key);
|
|
||||||
* const ciphertextParams = SerializableCipher.encrypt(AESAlgo, message, key, { iv: iv });
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
static encrypt(cipher, message, key, cfg) {
|
|
||||||
const _cfg = Object.assign({}, this.cfg, cfg);
|
|
||||||
const encryptor = cipher.createEncryptor(key, _cfg);
|
|
||||||
const ciphertext = encryptor.finalize(message);
|
|
||||||
const cipherCfg = encryptor.cfg;
|
|
||||||
return CipherParams.create({
|
|
||||||
ciphertext,
|
|
||||||
key,
|
|
||||||
iv: cipherCfg.iv,
|
|
||||||
algorithm: cipher,
|
|
||||||
mode: cipherCfg.mode,
|
|
||||||
padding: cipherCfg.padding,
|
|
||||||
blockSize: encryptor.blockSize,
|
|
||||||
formatter: _cfg.format || OpenSSLFormatter
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Decrypts serialized ciphertext.
|
|
||||||
*
|
|
||||||
* @param cipher - The cipher algorithm to use
|
|
||||||
* @param ciphertext - The ciphertext to decrypt
|
|
||||||
* @param key - The key
|
|
||||||
* @param cfg - Configuration options to use for this operation
|
|
||||||
* @returns The plaintext
|
|
||||||
* @static
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const plaintext = SerializableCipher.decrypt(AESAlgo, formattedCiphertext, key, { iv: iv });
|
|
||||||
* const plaintext = SerializableCipher.decrypt(AESAlgo, ciphertextParams, key, { iv: iv });
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
static decrypt(cipher, ciphertext, key, cfg) {
|
|
||||||
const _cfg = Object.assign({}, this.cfg, cfg);
|
|
||||||
const _ciphertext = this._parse(ciphertext, _cfg.format);
|
|
||||||
const plaintext = cipher.createDecryptor(key, _cfg).finalize(_ciphertext.ciphertext);
|
|
||||||
return plaintext;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Converts serialized ciphertext to CipherParams.
|
|
||||||
*
|
|
||||||
* @param ciphertext - The ciphertext
|
|
||||||
* @param format - The formatting strategy to use to parse serialized ciphertext
|
|
||||||
* @returns The unserialized ciphertext
|
|
||||||
* @static
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
static _parse(ciphertext, format) {
|
|
||||||
if (typeof ciphertext === "string") {
|
|
||||||
if (!format) throw new Error("Format required to parse string");
|
|
||||||
return format.parse(ciphertext, this);
|
|
||||||
}
|
|
||||||
if (ciphertext instanceof CipherParams) return ciphertext;
|
|
||||||
return new CipherParams(ciphertext);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* OpenSSL key derivation function.
|
|
||||||
* Derives a key and IV from a password using the OpenSSL method.
|
|
||||||
*/
|
|
||||||
const OpenSSLKdf = { execute(password, keySize, ivSize, salt, hasher) {
|
|
||||||
let _salt;
|
|
||||||
if (!salt) _salt = WordArray.random(64 / 8);
|
|
||||||
else if (typeof salt === "string") _salt = Hex.parse(salt);
|
|
||||||
else _salt = salt;
|
|
||||||
let key;
|
|
||||||
if (!hasher) key = EvpKDFAlgo.create({ keySize: keySize + ivSize }).compute(password, _salt);
|
|
||||||
else key = EvpKDFAlgo.create({
|
|
||||||
keySize: keySize + ivSize,
|
|
||||||
hasher
|
|
||||||
}).compute(password, _salt);
|
|
||||||
const iv = WordArray.create(key.words.slice(keySize), ivSize * 4);
|
|
||||||
key.sigBytes = keySize * 4;
|
|
||||||
return CipherParams.create({
|
|
||||||
key,
|
|
||||||
iv,
|
|
||||||
salt: _salt
|
|
||||||
});
|
|
||||||
} };
|
|
||||||
/**
|
|
||||||
* A serializable cipher wrapper that derives the key from a password.
|
|
||||||
* Returns ciphertext as a serializable cipher params object.
|
|
||||||
*/
|
|
||||||
var PasswordBasedCipher = class extends SerializableCipher {
|
|
||||||
/** Configuration options */
|
|
||||||
static cfg = Object.assign({}, SerializableCipher.cfg, { kdf: OpenSSLKdf });
|
|
||||||
/**
|
|
||||||
* Encrypts a message using a password.
|
|
||||||
*
|
|
||||||
* @param cipher - The cipher algorithm to use
|
|
||||||
* @param message - The message to encrypt
|
|
||||||
* @param password - The password
|
|
||||||
* @param cfg - Configuration options to use for this operation
|
|
||||||
* @returns A cipher params object
|
|
||||||
* @static
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const ciphertextParams = PasswordBasedCipher.encrypt(AESAlgo, message, 'password');
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
static encrypt(cipher, message, password, cfg) {
|
|
||||||
const _cfg = Object.assign({}, this.cfg, cfg);
|
|
||||||
if (!_cfg.kdf) throw new Error("KDF required for password-based encryption");
|
|
||||||
const derivedParams = _cfg.kdf.execute(password, cipher.keySize || cipher.keySize, cipher.ivSize || cipher.ivSize, _cfg.salt, _cfg.hasher);
|
|
||||||
_cfg.iv = derivedParams.iv;
|
|
||||||
const ciphertext = SerializableCipher.encrypt.call(this, cipher, message, derivedParams.key, _cfg);
|
|
||||||
ciphertext.salt = derivedParams.salt;
|
|
||||||
return ciphertext;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Decrypts serialized ciphertext using a password.
|
|
||||||
*
|
|
||||||
* @param cipher - The cipher algorithm to use
|
|
||||||
* @param ciphertext - The ciphertext to decrypt
|
|
||||||
* @param password - The password
|
|
||||||
* @param cfg - Configuration options to use for this operation
|
|
||||||
* @returns The plaintext
|
|
||||||
* @static
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const plaintext = PasswordBasedCipher.decrypt(AESAlgo, formattedCiphertext, 'password');
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
static decrypt(cipher, ciphertext, password, cfg) {
|
|
||||||
const _cfg = Object.assign({}, this.cfg, cfg);
|
|
||||||
const _ciphertext = this._parse(ciphertext, _cfg.format);
|
|
||||||
if (!_cfg.kdf) throw new Error("KDF required for password-based decryption");
|
|
||||||
const derivedParams = _cfg.kdf.execute(password, cipher.keySize || cipher.keySize, cipher.ivSize || cipher.ivSize, _ciphertext.salt, _cfg.hasher);
|
|
||||||
_cfg.iv = derivedParams.iv;
|
|
||||||
const plaintext = SerializableCipher.decrypt.call(this, cipher, _ciphertext, derivedParams.key, _cfg);
|
|
||||||
return plaintext;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { BlockCipher, BlockCipherMode, CBC, Cipher, CipherParams, OpenSSLFormatter, OpenSSLKdf, PasswordBasedCipher, Pkcs7, SerializableCipher, StreamCipher };
|
|
||||||
//# sourceMappingURL=cipher-core.mjs.map
|
|
||||||
@@ -1,584 +0,0 @@
|
|||||||
//#region src/core.ts
|
|
||||||
const crypto = (typeof globalThis !== "undefined" ? globalThis : void 0)?.crypto || (typeof global !== "undefined" ? global : void 0)?.crypto || (typeof window !== "undefined" ? window : void 0)?.crypto || (typeof self !== "undefined" ? self : void 0)?.crypto || (typeof frames !== "undefined" ? frames : void 0)?.[0]?.crypto;
|
|
||||||
/**
|
|
||||||
* Random word array generator function
|
|
||||||
*/
|
|
||||||
let randomWordArray;
|
|
||||||
if (crypto) randomWordArray = (nBytes) => {
|
|
||||||
const words = [];
|
|
||||||
for (let i = 0; i < nBytes; i += 4) words.push(crypto.getRandomValues(new Uint32Array(1))[0]);
|
|
||||||
return new WordArray(words, nBytes);
|
|
||||||
};
|
|
||||||
else randomWordArray = (nBytes) => {
|
|
||||||
const words = [];
|
|
||||||
const r = (m_w) => {
|
|
||||||
let _m_w = m_w;
|
|
||||||
let _m_z = 987654321;
|
|
||||||
const mask = 4294967295;
|
|
||||||
return () => {
|
|
||||||
_m_z = 36969 * (_m_z & 65535) + (_m_z >> 16) & mask;
|
|
||||||
_m_w = 18e3 * (_m_w & 65535) + (_m_w >> 16) & mask;
|
|
||||||
let result = (_m_z << 16) + _m_w & mask;
|
|
||||||
result /= 4294967296;
|
|
||||||
result += .5;
|
|
||||||
return result * (Math.random() > .5 ? 1 : -1);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
let rcache;
|
|
||||||
for (let i = 0; i < nBytes; i += 4) {
|
|
||||||
const _r = r((rcache || Math.random()) * 4294967296);
|
|
||||||
rcache = _r() * 987654071;
|
|
||||||
words.push(_r() * 4294967296 | 0);
|
|
||||||
}
|
|
||||||
return new WordArray(words, nBytes);
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Base class for inheritance.
|
|
||||||
* Provides basic object-oriented programming utilities.
|
|
||||||
*/
|
|
||||||
var Base = class {
|
|
||||||
/**
|
|
||||||
* Creates a new instance of this class with the provided arguments.
|
|
||||||
* This is a factory method that provides an alternative to using 'new'.
|
|
||||||
*
|
|
||||||
* @param args - Arguments to pass to the constructor
|
|
||||||
* @returns A new instance of this class
|
|
||||||
* @static
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const instance = MyType.create(arg1, arg2);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
static create(...args) {
|
|
||||||
return new this(...args);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Copies properties from the provided object into this instance.
|
|
||||||
* Performs a shallow merge of properties.
|
|
||||||
*
|
|
||||||
* @param properties - The properties to mix in
|
|
||||||
* @returns This instance for method chaining
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* instance.mixIn({ field: 'value', another: 123 });
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
mixIn(properties) {
|
|
||||||
return Object.assign(this, properties);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Creates a deep copy of this object.
|
|
||||||
*
|
|
||||||
* @returns A clone of this instance
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const clone = instance.clone();
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
clone() {
|
|
||||||
const clone = new this.constructor();
|
|
||||||
Object.assign(clone, this);
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* An array of 32-bit words.
|
|
||||||
* This is the core data structure used throughout the library for representing binary data.
|
|
||||||
*
|
|
||||||
* @property words - The array of 32-bit words
|
|
||||||
* @property sigBytes - The number of significant bytes in this word array
|
|
||||||
*/
|
|
||||||
var WordArray = class extends Base {
|
|
||||||
/** The array of 32-bit words */
|
|
||||||
words;
|
|
||||||
/** The number of significant bytes in this word array */
|
|
||||||
sigBytes;
|
|
||||||
/**
|
|
||||||
* Initializes a newly created word array.
|
|
||||||
* Can accept various input formats including regular arrays, typed arrays, and ArrayBuffers.
|
|
||||||
*
|
|
||||||
* @param words - An array of 32-bit words, typed array, or ArrayBuffer
|
|
||||||
* @param sigBytes - The number of significant bytes in the words (defaults to words.length * 4)
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const wordArray = new WordArray();
|
|
||||||
* const wordArray = new WordArray([0x00010203, 0x04050607]);
|
|
||||||
* const wordArray = new WordArray([0x00010203, 0x04050607], 6);
|
|
||||||
* const wordArray = new WordArray(new Uint8Array([1, 2, 3, 4]));
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
constructor(words = [], sigBytes) {
|
|
||||||
super();
|
|
||||||
if (words instanceof ArrayBuffer) {
|
|
||||||
const typedArray = new Uint8Array(words);
|
|
||||||
this._initFromUint8Array(typedArray);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ArrayBuffer.isView(words)) {
|
|
||||||
let uint8Array;
|
|
||||||
if (words instanceof Uint8Array) uint8Array = words;
|
|
||||||
else uint8Array = new Uint8Array(words.buffer, words.byteOffset, words.byteLength);
|
|
||||||
this._initFromUint8Array(uint8Array);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.words = words;
|
|
||||||
this.sigBytes = sigBytes !== void 0 ? sigBytes : this.words.length * 4;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Initialize from Uint8Array
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_initFromUint8Array(typedArray) {
|
|
||||||
const typedArrayByteLength = typedArray.byteLength;
|
|
||||||
const words = [];
|
|
||||||
for (let i = 0; i < typedArrayByteLength; i += 1) words[i >>> 2] |= typedArray[i] << 24 - i % 4 * 8;
|
|
||||||
this.words = words;
|
|
||||||
this.sigBytes = typedArrayByteLength;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Creates a word array filled with cryptographically strong random bytes.
|
|
||||||
* Uses Web Crypto API if available, falls back to Math.random() if not.
|
|
||||||
*
|
|
||||||
* @param nBytes - The number of random bytes to generate
|
|
||||||
* @returns The random word array
|
|
||||||
* @static
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const randomBytes = WordArray.random(16); // Generate 16 random bytes
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
static random = randomWordArray;
|
|
||||||
/**
|
|
||||||
* Converts this word array to a string using the specified encoding.
|
|
||||||
*
|
|
||||||
* @param encoder - The encoding strategy to use (defaults to Hex)
|
|
||||||
* @returns The stringified word array
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const hexString = wordArray.toString();
|
|
||||||
* const base64String = wordArray.toString(Base64);
|
|
||||||
* const utf8String = wordArray.toString(Utf8);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
toString(encoder = Hex) {
|
|
||||||
return encoder.stringify(this);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Concatenates a word array to this word array.
|
|
||||||
* Modifies this word array in place.
|
|
||||||
*
|
|
||||||
* @param wordArray - The word array to append
|
|
||||||
* @returns This word array for method chaining
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* wordArray1.concat(wordArray2);
|
|
||||||
* const combined = wordArray1.concat(wordArray2).concat(wordArray3);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
concat(wordArray) {
|
|
||||||
const thisWords = this.words;
|
|
||||||
const thatWords = wordArray.words;
|
|
||||||
const thisSigBytes = this.sigBytes;
|
|
||||||
const thatSigBytes = wordArray.sigBytes;
|
|
||||||
this.clamp();
|
|
||||||
if (thisSigBytes % 4) for (let i = 0; i < thatSigBytes; i += 1) {
|
|
||||||
const thatByte = thatWords[i >>> 2] >>> 24 - i % 4 * 8 & 255;
|
|
||||||
thisWords[thisSigBytes + i >>> 2] |= thatByte << 24 - (thisSigBytes + i) % 4 * 8;
|
|
||||||
}
|
|
||||||
else for (let i = 0; i < thatSigBytes; i += 4) thisWords[thisSigBytes + i >>> 2] = thatWords[i >>> 2];
|
|
||||||
this.sigBytes += thatSigBytes;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Removes insignificant bits from the end of the word array.
|
|
||||||
* This ensures the word array only contains the exact number of significant bytes.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* wordArray.clamp();
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
clamp() {
|
|
||||||
const { words, sigBytes } = this;
|
|
||||||
words[sigBytes >>> 2] &= 4294967295 << 32 - sigBytes % 4 * 8;
|
|
||||||
words.length = Math.ceil(sigBytes / 4);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Creates a copy of this word array.
|
|
||||||
*
|
|
||||||
* @returns The cloned word array
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const clone = wordArray.clone();
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
clone() {
|
|
||||||
const clone = super.clone();
|
|
||||||
clone.words = this.words.slice(0);
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Hex encoding strategy.
|
|
||||||
* Converts between word arrays and hexadecimal strings.
|
|
||||||
*/
|
|
||||||
const Hex = {
|
|
||||||
stringify(wordArray) {
|
|
||||||
const { words, sigBytes } = wordArray;
|
|
||||||
const hexChars = [];
|
|
||||||
for (let i = 0; i < sigBytes; i += 1) {
|
|
||||||
const bite = words[i >>> 2] >>> 24 - i % 4 * 8 & 255;
|
|
||||||
hexChars.push((bite >>> 4).toString(16));
|
|
||||||
hexChars.push((bite & 15).toString(16));
|
|
||||||
}
|
|
||||||
return hexChars.join("");
|
|
||||||
},
|
|
||||||
parse(hexStr) {
|
|
||||||
const hexStrLength = hexStr.length;
|
|
||||||
const words = [];
|
|
||||||
for (let i = 0; i < hexStrLength; i += 2) words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << 24 - i % 8 * 4;
|
|
||||||
return new WordArray(words, hexStrLength / 2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Latin1 encoding strategy.
|
|
||||||
* Converts between word arrays and Latin-1 (ISO-8859-1) strings.
|
|
||||||
*/
|
|
||||||
const Latin1 = {
|
|
||||||
stringify(wordArray) {
|
|
||||||
const { words, sigBytes } = wordArray;
|
|
||||||
const latin1Chars = [];
|
|
||||||
for (let i = 0; i < sigBytes; i += 1) {
|
|
||||||
const bite = words[i >>> 2] >>> 24 - i % 4 * 8 & 255;
|
|
||||||
latin1Chars.push(String.fromCharCode(bite));
|
|
||||||
}
|
|
||||||
return latin1Chars.join("");
|
|
||||||
},
|
|
||||||
parse(latin1Str) {
|
|
||||||
const latin1StrLength = latin1Str.length;
|
|
||||||
const words = [];
|
|
||||||
for (let i = 0; i < latin1StrLength; i += 1) words[i >>> 2] |= (latin1Str.charCodeAt(i) & 255) << 24 - i % 4 * 8;
|
|
||||||
return new WordArray(words, latin1StrLength);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* UTF-8 encoding strategy.
|
|
||||||
* Converts between word arrays and UTF-8 strings.
|
|
||||||
*/
|
|
||||||
const Utf8 = {
|
|
||||||
stringify(wordArray) {
|
|
||||||
try {
|
|
||||||
return decodeURIComponent(escape(Latin1.stringify(wordArray)));
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error("Malformed UTF-8 data");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
parse(utf8Str) {
|
|
||||||
return Latin1.parse(unescape(encodeURIComponent(utf8Str)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Abstract buffered block algorithm template.
|
|
||||||
* Provides a base implementation for algorithms that process data in fixed-size blocks.
|
|
||||||
*
|
|
||||||
* @property _minBufferSize - The number of blocks that should be kept unprocessed in the buffer
|
|
||||||
*/
|
|
||||||
var BufferedBlockAlgorithm = class extends Base {
|
|
||||||
/** The number of blocks that should be kept unprocessed in the buffer */
|
|
||||||
_minBufferSize = 0;
|
|
||||||
/** The data buffer */
|
|
||||||
_data;
|
|
||||||
/** The number of bytes in the data buffer */
|
|
||||||
_nDataBytes;
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Resets this block algorithm's data buffer to its initial state.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* bufferedBlockAlgorithm.reset();
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
reset() {
|
|
||||||
this._data = new WordArray();
|
|
||||||
this._nDataBytes = 0;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Adds new data to this block algorithm's buffer.
|
|
||||||
*
|
|
||||||
* @param data - The data to append (strings are converted to WordArray using UTF-8)
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* bufferedBlockAlgorithm._append('data');
|
|
||||||
* bufferedBlockAlgorithm._append(wordArray);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
_append(data) {
|
|
||||||
let m_data;
|
|
||||||
if (typeof data === "string") m_data = Utf8.parse(data);
|
|
||||||
else m_data = data;
|
|
||||||
this._data.concat(m_data);
|
|
||||||
this._nDataBytes += m_data.sigBytes;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Processes available data blocks.
|
|
||||||
* This method invokes _doProcessBlock(dataWords, offset), which must be implemented by a concrete subtype.
|
|
||||||
*
|
|
||||||
* @param doFlush - Whether all blocks and partial blocks should be processed
|
|
||||||
* @returns The processed data
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const processedData = bufferedBlockAlgorithm._process();
|
|
||||||
* const processedData = bufferedBlockAlgorithm._process(true); // Flush
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
_process(doFlush) {
|
|
||||||
let processedWords;
|
|
||||||
const data = this._data;
|
|
||||||
const dataWords = data.words;
|
|
||||||
const dataSigBytes = data.sigBytes;
|
|
||||||
const blockSizeBytes = this.blockSize * 4;
|
|
||||||
let nBlocksReady = dataSigBytes / blockSizeBytes;
|
|
||||||
if (doFlush) nBlocksReady = Math.ceil(nBlocksReady);
|
|
||||||
else nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0);
|
|
||||||
const nWordsReady = nBlocksReady * this.blockSize;
|
|
||||||
const nBytesReady = Math.min(nWordsReady * 4, dataSigBytes);
|
|
||||||
if (nWordsReady) {
|
|
||||||
for (let offset = 0; offset < nWordsReady; offset += this.blockSize) this._doProcessBlock(dataWords, offset);
|
|
||||||
processedWords = dataWords.splice(0, nWordsReady);
|
|
||||||
data.sigBytes -= nBytesReady;
|
|
||||||
}
|
|
||||||
return new WordArray(processedWords || [], nBytesReady);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Creates a copy of this object.
|
|
||||||
*
|
|
||||||
* @returns The clone
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const clone = bufferedBlockAlgorithm.clone();
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
clone() {
|
|
||||||
const clone = super.clone();
|
|
||||||
clone._data = this._data.clone();
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Abstract hasher template.
|
|
||||||
* Base class for all hash algorithm implementations.
|
|
||||||
*
|
|
||||||
* @property blockSize - The number of 32-bit words this hasher operates on (default: 16 = 512 bits)
|
|
||||||
*/
|
|
||||||
var Hasher = class extends BufferedBlockAlgorithm {
|
|
||||||
/** The number of 32-bit words this hasher operates on */
|
|
||||||
blockSize = 512 / 32;
|
|
||||||
/** Configuration options */
|
|
||||||
cfg;
|
|
||||||
/** The hash result */
|
|
||||||
_hash;
|
|
||||||
/**
|
|
||||||
* Initializes a newly created hasher.
|
|
||||||
*
|
|
||||||
* @param cfg - Configuration options
|
|
||||||
*/
|
|
||||||
constructor(cfg) {
|
|
||||||
super();
|
|
||||||
this.cfg = Object.assign({}, cfg);
|
|
||||||
this.reset();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Creates a shortcut function to a hasher's object interface.
|
|
||||||
*
|
|
||||||
* @param SubHasher - The hasher class to create a helper for
|
|
||||||
* @returns The shortcut function
|
|
||||||
* @static
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const SHA256 = Hasher._createHelper(SHA256Algo);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
static _createHelper(SubHasher) {
|
|
||||||
return (message, cfg) => {
|
|
||||||
return new SubHasher(cfg).finalize(message);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Creates a shortcut function to the HMAC's object interface.
|
|
||||||
*
|
|
||||||
* @param SubHasher - The hasher class to use in this HMAC helper
|
|
||||||
* @returns The shortcut function
|
|
||||||
* @static
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const HmacSHA256 = Hasher._createHmacHelper(SHA256Algo);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
static _createHmacHelper(SubHasher) {
|
|
||||||
return (message, key) => {
|
|
||||||
return new HMAC(SubHasher, key).finalize(message);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Resets this hasher to its initial state.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* hasher.reset();
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
reset() {
|
|
||||||
super.reset();
|
|
||||||
this._doReset();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Updates this hasher with a message.
|
|
||||||
*
|
|
||||||
* @param messageUpdate - The message to append
|
|
||||||
* @returns This hasher instance for method chaining
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* hasher.update('message');
|
|
||||||
* hasher.update(wordArray);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
update(messageUpdate) {
|
|
||||||
this._append(messageUpdate);
|
|
||||||
this._process();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Finalizes the hash computation.
|
|
||||||
* Note that the finalize operation is effectively a destructive, read-once operation.
|
|
||||||
*
|
|
||||||
* @param messageUpdate - An optional final message update
|
|
||||||
* @returns The computed hash
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const hash = hasher.finalize();
|
|
||||||
* const hash = hasher.finalize('message');
|
|
||||||
* const hash = hasher.finalize(wordArray);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
finalize(messageUpdate) {
|
|
||||||
if (messageUpdate) this._append(messageUpdate);
|
|
||||||
const hash = this._doFinalize();
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Base class for 32-bit hash algorithms.
|
|
||||||
* Hash algorithms that operate on 32-bit words should extend this class.
|
|
||||||
*/
|
|
||||||
var Hasher32 = class extends Hasher {};
|
|
||||||
/**
|
|
||||||
* Base class for 64-bit hash algorithms.
|
|
||||||
* Hash algorithms that operate on 64-bit words should extend this class.
|
|
||||||
*/
|
|
||||||
var Hasher64 = class extends Hasher {};
|
|
||||||
/**
|
|
||||||
* HMAC (Hash-based Message Authentication Code) algorithm.
|
|
||||||
* Provides message authentication using a cryptographic hash function and a secret key.
|
|
||||||
*/
|
|
||||||
var HMAC = class HMAC extends Base {
|
|
||||||
/** The inner hasher instance */
|
|
||||||
_hasher;
|
|
||||||
/** The outer key */
|
|
||||||
_oKey;
|
|
||||||
/** The inner key */
|
|
||||||
_iKey;
|
|
||||||
/**
|
|
||||||
* Initializes a newly created HMAC.
|
|
||||||
*
|
|
||||||
* @param SubHasher - The hash algorithm class to use
|
|
||||||
* @param key - The secret key
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const hmac = new HMAC(SHA256Algo, 'secret key');
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
constructor(SubHasher, key) {
|
|
||||||
super();
|
|
||||||
const hasher = new SubHasher();
|
|
||||||
this._hasher = hasher;
|
|
||||||
let _key;
|
|
||||||
if (typeof key === "string") _key = Utf8.parse(key);
|
|
||||||
else _key = key;
|
|
||||||
const hasherBlockSize = hasher.blockSize;
|
|
||||||
const hasherBlockSizeBytes = hasherBlockSize * 4;
|
|
||||||
if (_key.sigBytes > hasherBlockSizeBytes) _key = hasher.finalize(_key);
|
|
||||||
_key.clamp();
|
|
||||||
const oKey = _key.clone();
|
|
||||||
this._oKey = oKey;
|
|
||||||
const iKey = _key.clone();
|
|
||||||
this._iKey = iKey;
|
|
||||||
const oKeyWords = oKey.words;
|
|
||||||
const iKeyWords = iKey.words;
|
|
||||||
for (let i = 0; i < hasherBlockSize; i += 1) {
|
|
||||||
oKeyWords[i] ^= 1549556828;
|
|
||||||
iKeyWords[i] ^= 909522486;
|
|
||||||
}
|
|
||||||
oKey.sigBytes = hasherBlockSizeBytes;
|
|
||||||
iKey.sigBytes = hasherBlockSizeBytes;
|
|
||||||
this.reset();
|
|
||||||
}
|
|
||||||
static create(...args) {
|
|
||||||
const [SubHasher, key] = args;
|
|
||||||
return new HMAC(SubHasher, key);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Resets this HMAC to its initial state.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* hmac.reset();
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
reset() {
|
|
||||||
const hasher = this._hasher;
|
|
||||||
hasher.reset();
|
|
||||||
hasher.update(this._iKey);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Updates this HMAC with a message.
|
|
||||||
*
|
|
||||||
* @param messageUpdate - The message to append
|
|
||||||
* @returns This HMAC instance for method chaining
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* hmac.update('message');
|
|
||||||
* hmac.update(wordArray);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
update(messageUpdate) {
|
|
||||||
this._hasher.update(messageUpdate);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Finalizes the HMAC computation.
|
|
||||||
* Note that the finalize operation is effectively a destructive, read-once operation.
|
|
||||||
*
|
|
||||||
* @param messageUpdate - An optional final message update
|
|
||||||
* @returns The computed HMAC
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const hmacValue = hmac.finalize();
|
|
||||||
* const hmacValue = hmac.finalize('message');
|
|
||||||
* const hmacValue = hmac.finalize(wordArray);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
finalize(messageUpdate) {
|
|
||||||
const hasher = this._hasher;
|
|
||||||
const innerHash = hasher.finalize(messageUpdate);
|
|
||||||
hasher.reset();
|
|
||||||
const hmac = hasher.finalize(this._oKey.clone().concat(innerHash));
|
|
||||||
return hmac;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { Base, BufferedBlockAlgorithm, HMAC, Hasher, Hasher32, Hasher64, Hex, Latin1, Utf8, WordArray };
|
|
||||||
//# sourceMappingURL=core.mjs.map
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
import { WordArray } from "./core.mjs";
|
|
||||||
|
|
||||||
//#region src/enc-base64.ts
|
|
||||||
/**
|
|
||||||
* Parses a Base64 string to a WordArray.
|
|
||||||
* Helper function for Base64 decoding.
|
|
||||||
*
|
|
||||||
* @param base64Str - The Base64 string to parse
|
|
||||||
* @param base64StrLength - The length of the Base64 string
|
|
||||||
* @param reverseMap - The reverse character map for decoding
|
|
||||||
* @returns The decoded WordArray
|
|
||||||
*/
|
|
||||||
const parseLoop = (base64Str, base64StrLength, reverseMap) => {
|
|
||||||
const words = [];
|
|
||||||
let nBytes = 0;
|
|
||||||
for (let i = 0; i < base64StrLength; i += 1) if (i % 4) {
|
|
||||||
const bits1 = reverseMap[base64Str.charCodeAt(i - 1)] << i % 4 * 2;
|
|
||||||
const bits2 = reverseMap[base64Str.charCodeAt(i)] >>> 6 - i % 4 * 2;
|
|
||||||
const bitsCombined = bits1 | bits2;
|
|
||||||
words[nBytes >>> 2] |= bitsCombined << 24 - nBytes % 4 * 8;
|
|
||||||
nBytes += 1;
|
|
||||||
}
|
|
||||||
return WordArray.create(words, nBytes);
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Base64 encoding strategy implementation.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
var Base64Impl = class {
|
|
||||||
/** The Base64 character map */
|
|
||||||
_map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
|
||||||
/** The reverse character map for decoding */
|
|
||||||
_reverseMap;
|
|
||||||
/**
|
|
||||||
* Converts a word array to a Base64 string.
|
|
||||||
*
|
|
||||||
* @param wordArray - The word array to convert
|
|
||||||
* @returns The Base64 string representation
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const base64String = Base64.stringify(wordArray);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
stringify(wordArray) {
|
|
||||||
const { words, sigBytes } = wordArray;
|
|
||||||
const map = this._map;
|
|
||||||
wordArray.clamp();
|
|
||||||
const base64Chars = [];
|
|
||||||
for (let i = 0; i < sigBytes; i += 3) {
|
|
||||||
const byte1 = words[i >>> 2] >>> 24 - i % 4 * 8 & 255;
|
|
||||||
const byte2 = words[i + 1 >>> 2] >>> 24 - (i + 1) % 4 * 8 & 255;
|
|
||||||
const byte3 = words[i + 2 >>> 2] >>> 24 - (i + 2) % 4 * 8 & 255;
|
|
||||||
const triplet = byte1 << 16 | byte2 << 8 | byte3;
|
|
||||||
for (let j = 0; j < 4 && i + j * .75 < sigBytes; j += 1) base64Chars.push(map.charAt(triplet >>> 6 * (3 - j) & 63));
|
|
||||||
}
|
|
||||||
const paddingChar = map.charAt(64);
|
|
||||||
if (paddingChar) while (base64Chars.length % 4) base64Chars.push(paddingChar);
|
|
||||||
return base64Chars.join("");
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Converts a Base64 string to a word array.
|
|
||||||
*
|
|
||||||
* @param base64Str - The Base64 string to parse
|
|
||||||
* @returns The word array representation
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const wordArray = Base64.parse(base64String);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
parse(base64Str) {
|
|
||||||
let base64StrLength = base64Str.length;
|
|
||||||
const map = this._map;
|
|
||||||
let reverseMap = this._reverseMap;
|
|
||||||
if (!reverseMap) {
|
|
||||||
this._reverseMap = [];
|
|
||||||
reverseMap = this._reverseMap;
|
|
||||||
for (let j = 0; j < map.length; j += 1) reverseMap[map.charCodeAt(j)] = j;
|
|
||||||
}
|
|
||||||
const paddingChar = map.charAt(64);
|
|
||||||
if (paddingChar) {
|
|
||||||
const paddingIndex = base64Str.indexOf(paddingChar);
|
|
||||||
if (paddingIndex !== -1) base64StrLength = paddingIndex;
|
|
||||||
}
|
|
||||||
return parseLoop(base64Str, base64StrLength, reverseMap);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Base64 encoding strategy.
|
|
||||||
* Converts between WordArrays and Base64 strings.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* // Encoding
|
|
||||||
* const base64String = Base64.stringify(wordArray);
|
|
||||||
*
|
|
||||||
* // Decoding
|
|
||||||
* const wordArray = Base64.parse(base64String);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
const Base64 = new Base64Impl();
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { Base64, parseLoop };
|
|
||||||
//# sourceMappingURL=enc-base64.mjs.map
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
import { parseLoop } from "./enc-base64.mjs";
|
|
||||||
|
|
||||||
//#region src/enc-base64url.ts
|
|
||||||
/**
|
|
||||||
* Base64url encoding strategy implementation.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
var Base64urlImpl = class {
|
|
||||||
/** Standard Base64 character map */
|
|
||||||
_map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
|
||||||
/** URL-safe Base64 character map (no padding) */
|
|
||||||
_safeMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
||||||
/** Reverse character map for decoding */
|
|
||||||
_reverseMap;
|
|
||||||
/**
|
|
||||||
* Converts a word array to a Base64url string.
|
|
||||||
*
|
|
||||||
* @param wordArray - The word array to convert
|
|
||||||
* @param urlSafe - Whether to use URL-safe encoding (default: true)
|
|
||||||
* @returns The Base64url string representation
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* // URL-safe encoding (default)
|
|
||||||
* const base64urlString = Base64url.stringify(wordArray);
|
|
||||||
*
|
|
||||||
* // Standard Base64 encoding
|
|
||||||
* const base64String = Base64url.stringify(wordArray, false);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
stringify(wordArray, urlSafe = true) {
|
|
||||||
const { words, sigBytes } = wordArray;
|
|
||||||
const map = urlSafe ? this._safeMap : this._map;
|
|
||||||
wordArray.clamp();
|
|
||||||
const base64Chars = [];
|
|
||||||
for (let i = 0; i < sigBytes; i += 3) {
|
|
||||||
const byte1 = words[i >>> 2] >>> 24 - i % 4 * 8 & 255;
|
|
||||||
const byte2 = words[i + 1 >>> 2] >>> 24 - (i + 1) % 4 * 8 & 255;
|
|
||||||
const byte3 = words[i + 2 >>> 2] >>> 24 - (i + 2) % 4 * 8 & 255;
|
|
||||||
const triplet = byte1 << 16 | byte2 << 8 | byte3;
|
|
||||||
for (let j = 0; j < 4 && i + j * .75 < sigBytes; j += 1) base64Chars.push(map.charAt(triplet >>> 6 * (3 - j) & 63));
|
|
||||||
}
|
|
||||||
const paddingChar = map.charAt(64);
|
|
||||||
if (paddingChar) while (base64Chars.length % 4) base64Chars.push(paddingChar);
|
|
||||||
return base64Chars.join("");
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Converts a Base64url string to a word array.
|
|
||||||
*
|
|
||||||
* @param base64Str - The Base64url string to parse
|
|
||||||
* @param urlSafe - Whether to use URL-safe decoding (default: true)
|
|
||||||
* @returns The word array representation
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* // URL-safe decoding (default)
|
|
||||||
* const wordArray = Base64url.parse(base64urlString);
|
|
||||||
*
|
|
||||||
* // Standard Base64 decoding
|
|
||||||
* const wordArray = Base64url.parse(base64String, false);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
parse(base64Str, urlSafe = true) {
|
|
||||||
let base64StrLength = base64Str.length;
|
|
||||||
const map = urlSafe ? this._safeMap : this._map;
|
|
||||||
let reverseMap = this._reverseMap;
|
|
||||||
if (!reverseMap) {
|
|
||||||
this._reverseMap = [];
|
|
||||||
reverseMap = this._reverseMap;
|
|
||||||
for (let j = 0; j < map.length; j += 1) reverseMap[map.charCodeAt(j)] = j;
|
|
||||||
}
|
|
||||||
const paddingChar = map.charAt(64);
|
|
||||||
if (paddingChar) {
|
|
||||||
const paddingIndex = base64Str.indexOf(paddingChar);
|
|
||||||
if (paddingIndex !== -1) base64StrLength = paddingIndex;
|
|
||||||
}
|
|
||||||
return parseLoop(base64Str, base64StrLength, reverseMap);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Base64url encoding strategy.
|
|
||||||
* Provides URL-safe Base64 encoding/decoding that can be used in URLs without escaping.
|
|
||||||
*
|
|
||||||
* The URL-safe variant:
|
|
||||||
* - Uses '-' instead of '+'
|
|
||||||
* - Uses '_' instead of '/'
|
|
||||||
* - Omits padding '=' characters
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* // URL-safe encoding (default)
|
|
||||||
* const urlSafeString = Base64url.stringify(wordArray);
|
|
||||||
* const wordArray = Base64url.parse(urlSafeString);
|
|
||||||
*
|
|
||||||
* // Standard Base64 encoding
|
|
||||||
* const base64String = Base64url.stringify(wordArray, false);
|
|
||||||
* const wordArray = Base64url.parse(base64String, false);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
const Base64url = new Base64urlImpl();
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { Base64url };
|
|
||||||
//# sourceMappingURL=enc-base64url.mjs.map
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import { WordArray } from "./core.mjs";
|
|
||||||
|
|
||||||
//#region src/enc-utf16.ts
|
|
||||||
/**
|
|
||||||
* Swaps endian of a word
|
|
||||||
* @param word - The word to swap
|
|
||||||
* @returns The word with swapped endian
|
|
||||||
*/
|
|
||||||
const swapEndian = (word) => word << 8 & 4278255360 | word >>> 8 & 16711935;
|
|
||||||
/**
|
|
||||||
* UTF-16 BE encoding strategy.
|
|
||||||
*/
|
|
||||||
const Utf16BE = {
|
|
||||||
stringify(wordArray) {
|
|
||||||
const { words, sigBytes } = wordArray;
|
|
||||||
const utf16Chars = [];
|
|
||||||
for (let i = 0; i < sigBytes; i += 2) {
|
|
||||||
const codePoint = words[i >>> 2] >>> 16 - i % 4 * 8 & 65535;
|
|
||||||
utf16Chars.push(String.fromCharCode(codePoint));
|
|
||||||
}
|
|
||||||
return utf16Chars.join("");
|
|
||||||
},
|
|
||||||
parse(utf16Str) {
|
|
||||||
const utf16StrLength = utf16Str.length;
|
|
||||||
const words = [];
|
|
||||||
for (let i = 0; i < utf16StrLength; i += 1) words[i >>> 1] |= utf16Str.charCodeAt(i) << 16 - i % 2 * 16;
|
|
||||||
return WordArray.create(words, utf16StrLength * 2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* UTF-16 encoding strategy (defaults to UTF-16 BE).
|
|
||||||
*/
|
|
||||||
const Utf16 = Utf16BE;
|
|
||||||
/**
|
|
||||||
* UTF-16 LE encoding strategy.
|
|
||||||
*/
|
|
||||||
const Utf16LE = {
|
|
||||||
stringify(wordArray) {
|
|
||||||
const { words, sigBytes } = wordArray;
|
|
||||||
const utf16Chars = [];
|
|
||||||
for (let i = 0; i < sigBytes; i += 2) {
|
|
||||||
const codePoint = swapEndian(words[i >>> 2] >>> 16 - i % 4 * 8 & 65535);
|
|
||||||
utf16Chars.push(String.fromCharCode(codePoint));
|
|
||||||
}
|
|
||||||
return utf16Chars.join("");
|
|
||||||
},
|
|
||||||
parse(utf16Str) {
|
|
||||||
const utf16StrLength = utf16Str.length;
|
|
||||||
const words = [];
|
|
||||||
for (let i = 0; i < utf16StrLength; i += 1) words[i >>> 1] |= swapEndian(utf16Str.charCodeAt(i) << 16 - i % 2 * 16);
|
|
||||||
return WordArray.create(words, utf16StrLength * 2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { Utf16, Utf16BE, Utf16LE };
|
|
||||||
//# sourceMappingURL=enc-utf16.mjs.map
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
import { Base, WordArray } from "./core.mjs";
|
|
||||||
import { MD5Algo } from "./md5.mjs";
|
|
||||||
|
|
||||||
//#region src/evpkdf.ts
|
|
||||||
/**
|
|
||||||
* This key derivation function is meant to conform with EVP_BytesToKey.
|
|
||||||
* www.openssl.org/docs/crypto/EVP_BytesToKey.html
|
|
||||||
*/
|
|
||||||
var EvpKDFAlgo = class extends Base {
|
|
||||||
cfg;
|
|
||||||
/**
|
|
||||||
* Initializes a newly created key derivation function.
|
|
||||||
*
|
|
||||||
* @param {Object} cfg (Optional) The configuration options to use for the derivation.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* const kdf = new EvpKDFAlgo();
|
|
||||||
* const kdf = new EvpKDFAlgo({ keySize: 8 });
|
|
||||||
* const kdf = new EvpKDFAlgo({ keySize: 8, iterations: 1000 });
|
|
||||||
*/
|
|
||||||
constructor(cfg) {
|
|
||||||
super();
|
|
||||||
/**
|
|
||||||
* Configuration options.
|
|
||||||
*
|
|
||||||
* @property {number} keySize The key size in words to generate. Default: 4 (128 bits)
|
|
||||||
* @property {Hasher} hasher The hash algorithm to use. Default: MD5
|
|
||||||
* @property {number} iterations The number of iterations to perform. Default: 1
|
|
||||||
*/
|
|
||||||
this.cfg = Object.assign({}, {
|
|
||||||
keySize: 128 / 32,
|
|
||||||
hasher: MD5Algo,
|
|
||||||
iterations: 1
|
|
||||||
}, cfg);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Derives a key from a password.
|
|
||||||
*
|
|
||||||
* @param {WordArray|string} password The password.
|
|
||||||
* @param {WordArray|string} salt A salt.
|
|
||||||
*
|
|
||||||
* @return {WordArray} The derived key.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* const key = kdf.compute(password, salt);
|
|
||||||
*/
|
|
||||||
compute(password, salt) {
|
|
||||||
let block;
|
|
||||||
const { cfg } = this;
|
|
||||||
const hasher = new cfg.hasher();
|
|
||||||
const derivedKey = WordArray.create();
|
|
||||||
const derivedKeyWords = derivedKey.words;
|
|
||||||
const { keySize, iterations } = cfg;
|
|
||||||
while (derivedKeyWords.length < keySize) {
|
|
||||||
if (block) hasher.update(block);
|
|
||||||
block = hasher.update(password).finalize(salt);
|
|
||||||
hasher.reset();
|
|
||||||
for (let i = 1; i < iterations; i += 1) {
|
|
||||||
block = hasher.finalize(block);
|
|
||||||
hasher.reset();
|
|
||||||
}
|
|
||||||
derivedKey.concat(block);
|
|
||||||
}
|
|
||||||
derivedKey.sigBytes = keySize * 4;
|
|
||||||
return derivedKey;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Derives a key from a password.
|
|
||||||
*
|
|
||||||
* @param {WordArray|string} password The password.
|
|
||||||
* @param {WordArray|string} salt A salt.
|
|
||||||
* @param {Object} cfg (Optional) The configuration options to use for this computation.
|
|
||||||
*
|
|
||||||
* @return {WordArray} The derived key.
|
|
||||||
*
|
|
||||||
* @static
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* var key = EvpKDF(password, salt);
|
|
||||||
* var key = EvpKDF(password, salt, { keySize: 8 });
|
|
||||||
* var key = EvpKDF(password, salt, { keySize: 8, iterations: 1000 });
|
|
||||||
*/
|
|
||||||
const EvpKDF = (password, salt, cfg) => new EvpKDFAlgo(cfg).compute(password, salt);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { EvpKDF, EvpKDFAlgo };
|
|
||||||
//# sourceMappingURL=evpkdf.mjs.map
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { Hex } from "./core.mjs";
|
|
||||||
import { CipherParams } from "./cipher-core.mjs";
|
|
||||||
|
|
||||||
//#region src/format-hex.ts
|
|
||||||
/**
|
|
||||||
* Hex formatter for cipher params.
|
|
||||||
* Converts cipher params to/from hexadecimal strings.
|
|
||||||
*/
|
|
||||||
const HexFormatter = {
|
|
||||||
stringify(cipherParams) {
|
|
||||||
if (!cipherParams.ciphertext) throw new Error("Ciphertext is required");
|
|
||||||
return cipherParams.ciphertext.toString(Hex);
|
|
||||||
},
|
|
||||||
parse(input) {
|
|
||||||
const ciphertext = Hex.parse(input);
|
|
||||||
return CipherParams.create({ ciphertext });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { HexFormatter };
|
|
||||||
//# sourceMappingURL=format-hex.mjs.map
|
|
||||||
@@ -1,201 +0,0 @@
|
|||||||
import { Hasher, Hasher32, WordArray } from "./core.mjs";
|
|
||||||
|
|
||||||
//#region src/md5.ts
|
|
||||||
const T = /* @__PURE__ */ (() => {
|
|
||||||
const a = [];
|
|
||||||
for (let i = 0; i < 64; i += 1) a[i] = Math.abs(Math.sin(i + 1)) * 4294967296 | 0;
|
|
||||||
return a;
|
|
||||||
})();
|
|
||||||
/**
|
|
||||||
* MD5 round function F
|
|
||||||
*/
|
|
||||||
const FF = (a, b, c, d, x, s, t) => {
|
|
||||||
const n = a + (b & c | ~b & d) + x + t;
|
|
||||||
return (n << s | n >>> 32 - s) + b;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* MD5 round function G
|
|
||||||
*/
|
|
||||||
const GG = (a, b, c, d, x, s, t) => {
|
|
||||||
const n = a + (b & d | c & ~d) + x + t;
|
|
||||||
return (n << s | n >>> 32 - s) + b;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* MD5 round function H
|
|
||||||
*/
|
|
||||||
const HH = (a, b, c, d, x, s, t) => {
|
|
||||||
const n = a + (b ^ c ^ d) + x + t;
|
|
||||||
return (n << s | n >>> 32 - s) + b;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* MD5 round function I
|
|
||||||
*/
|
|
||||||
const II = (a, b, c, d, x, s, t) => {
|
|
||||||
const n = a + (c ^ (b | ~d)) + x + t;
|
|
||||||
return (n << s | n >>> 32 - s) + b;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* MD5 hash algorithm.
|
|
||||||
*/
|
|
||||||
var MD5Algo = class extends Hasher32 {
|
|
||||||
_doReset() {
|
|
||||||
this._hash = new WordArray([
|
|
||||||
1732584193,
|
|
||||||
4023233417,
|
|
||||||
2562383102,
|
|
||||||
271733878
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
_doProcessBlock(M, offset) {
|
|
||||||
const _M = M;
|
|
||||||
for (let i = 0; i < 16; i += 1) {
|
|
||||||
const offset_i = offset + i;
|
|
||||||
const M_offset_i = M[offset_i];
|
|
||||||
_M[offset_i] = (M_offset_i << 8 | M_offset_i >>> 24) & 16711935 | (M_offset_i << 24 | M_offset_i >>> 8) & 4278255360;
|
|
||||||
}
|
|
||||||
const H = this._hash.words;
|
|
||||||
const M_offset_0 = _M[offset + 0];
|
|
||||||
const M_offset_1 = _M[offset + 1];
|
|
||||||
const M_offset_2 = _M[offset + 2];
|
|
||||||
const M_offset_3 = _M[offset + 3];
|
|
||||||
const M_offset_4 = _M[offset + 4];
|
|
||||||
const M_offset_5 = _M[offset + 5];
|
|
||||||
const M_offset_6 = _M[offset + 6];
|
|
||||||
const M_offset_7 = _M[offset + 7];
|
|
||||||
const M_offset_8 = _M[offset + 8];
|
|
||||||
const M_offset_9 = _M[offset + 9];
|
|
||||||
const M_offset_10 = _M[offset + 10];
|
|
||||||
const M_offset_11 = _M[offset + 11];
|
|
||||||
const M_offset_12 = _M[offset + 12];
|
|
||||||
const M_offset_13 = _M[offset + 13];
|
|
||||||
const M_offset_14 = _M[offset + 14];
|
|
||||||
const M_offset_15 = _M[offset + 15];
|
|
||||||
let a = H[0];
|
|
||||||
let b = H[1];
|
|
||||||
let c = H[2];
|
|
||||||
let d = H[3];
|
|
||||||
a = FF(a, b, c, d, M_offset_0, 7, T[0]);
|
|
||||||
d = FF(d, a, b, c, M_offset_1, 12, T[1]);
|
|
||||||
c = FF(c, d, a, b, M_offset_2, 17, T[2]);
|
|
||||||
b = FF(b, c, d, a, M_offset_3, 22, T[3]);
|
|
||||||
a = FF(a, b, c, d, M_offset_4, 7, T[4]);
|
|
||||||
d = FF(d, a, b, c, M_offset_5, 12, T[5]);
|
|
||||||
c = FF(c, d, a, b, M_offset_6, 17, T[6]);
|
|
||||||
b = FF(b, c, d, a, M_offset_7, 22, T[7]);
|
|
||||||
a = FF(a, b, c, d, M_offset_8, 7, T[8]);
|
|
||||||
d = FF(d, a, b, c, M_offset_9, 12, T[9]);
|
|
||||||
c = FF(c, d, a, b, M_offset_10, 17, T[10]);
|
|
||||||
b = FF(b, c, d, a, M_offset_11, 22, T[11]);
|
|
||||||
a = FF(a, b, c, d, M_offset_12, 7, T[12]);
|
|
||||||
d = FF(d, a, b, c, M_offset_13, 12, T[13]);
|
|
||||||
c = FF(c, d, a, b, M_offset_14, 17, T[14]);
|
|
||||||
b = FF(b, c, d, a, M_offset_15, 22, T[15]);
|
|
||||||
a = GG(a, b, c, d, M_offset_1, 5, T[16]);
|
|
||||||
d = GG(d, a, b, c, M_offset_6, 9, T[17]);
|
|
||||||
c = GG(c, d, a, b, M_offset_11, 14, T[18]);
|
|
||||||
b = GG(b, c, d, a, M_offset_0, 20, T[19]);
|
|
||||||
a = GG(a, b, c, d, M_offset_5, 5, T[20]);
|
|
||||||
d = GG(d, a, b, c, M_offset_10, 9, T[21]);
|
|
||||||
c = GG(c, d, a, b, M_offset_15, 14, T[22]);
|
|
||||||
b = GG(b, c, d, a, M_offset_4, 20, T[23]);
|
|
||||||
a = GG(a, b, c, d, M_offset_9, 5, T[24]);
|
|
||||||
d = GG(d, a, b, c, M_offset_14, 9, T[25]);
|
|
||||||
c = GG(c, d, a, b, M_offset_3, 14, T[26]);
|
|
||||||
b = GG(b, c, d, a, M_offset_8, 20, T[27]);
|
|
||||||
a = GG(a, b, c, d, M_offset_13, 5, T[28]);
|
|
||||||
d = GG(d, a, b, c, M_offset_2, 9, T[29]);
|
|
||||||
c = GG(c, d, a, b, M_offset_7, 14, T[30]);
|
|
||||||
b = GG(b, c, d, a, M_offset_12, 20, T[31]);
|
|
||||||
a = HH(a, b, c, d, M_offset_5, 4, T[32]);
|
|
||||||
d = HH(d, a, b, c, M_offset_8, 11, T[33]);
|
|
||||||
c = HH(c, d, a, b, M_offset_11, 16, T[34]);
|
|
||||||
b = HH(b, c, d, a, M_offset_14, 23, T[35]);
|
|
||||||
a = HH(a, b, c, d, M_offset_1, 4, T[36]);
|
|
||||||
d = HH(d, a, b, c, M_offset_4, 11, T[37]);
|
|
||||||
c = HH(c, d, a, b, M_offset_7, 16, T[38]);
|
|
||||||
b = HH(b, c, d, a, M_offset_10, 23, T[39]);
|
|
||||||
a = HH(a, b, c, d, M_offset_13, 4, T[40]);
|
|
||||||
d = HH(d, a, b, c, M_offset_0, 11, T[41]);
|
|
||||||
c = HH(c, d, a, b, M_offset_3, 16, T[42]);
|
|
||||||
b = HH(b, c, d, a, M_offset_6, 23, T[43]);
|
|
||||||
a = HH(a, b, c, d, M_offset_9, 4, T[44]);
|
|
||||||
d = HH(d, a, b, c, M_offset_12, 11, T[45]);
|
|
||||||
c = HH(c, d, a, b, M_offset_15, 16, T[46]);
|
|
||||||
b = HH(b, c, d, a, M_offset_2, 23, T[47]);
|
|
||||||
a = II(a, b, c, d, M_offset_0, 6, T[48]);
|
|
||||||
d = II(d, a, b, c, M_offset_7, 10, T[49]);
|
|
||||||
c = II(c, d, a, b, M_offset_14, 15, T[50]);
|
|
||||||
b = II(b, c, d, a, M_offset_5, 21, T[51]);
|
|
||||||
a = II(a, b, c, d, M_offset_12, 6, T[52]);
|
|
||||||
d = II(d, a, b, c, M_offset_3, 10, T[53]);
|
|
||||||
c = II(c, d, a, b, M_offset_10, 15, T[54]);
|
|
||||||
b = II(b, c, d, a, M_offset_1, 21, T[55]);
|
|
||||||
a = II(a, b, c, d, M_offset_8, 6, T[56]);
|
|
||||||
d = II(d, a, b, c, M_offset_15, 10, T[57]);
|
|
||||||
c = II(c, d, a, b, M_offset_6, 15, T[58]);
|
|
||||||
b = II(b, c, d, a, M_offset_13, 21, T[59]);
|
|
||||||
a = II(a, b, c, d, M_offset_4, 6, T[60]);
|
|
||||||
d = II(d, a, b, c, M_offset_11, 10, T[61]);
|
|
||||||
c = II(c, d, a, b, M_offset_2, 15, T[62]);
|
|
||||||
b = II(b, c, d, a, M_offset_9, 21, T[63]);
|
|
||||||
H[0] = H[0] + a | 0;
|
|
||||||
H[1] = H[1] + b | 0;
|
|
||||||
H[2] = H[2] + c | 0;
|
|
||||||
H[3] = H[3] + d | 0;
|
|
||||||
}
|
|
||||||
_doFinalize() {
|
|
||||||
const data = this._data;
|
|
||||||
const dataWords = data.words;
|
|
||||||
const nBitsTotal = this._nDataBytes * 8;
|
|
||||||
const nBitsLeft = data.sigBytes * 8;
|
|
||||||
dataWords[nBitsLeft >>> 5] |= 128 << 24 - nBitsLeft % 32;
|
|
||||||
const nBitsTotalH = Math.floor(nBitsTotal / 4294967296);
|
|
||||||
const nBitsTotalL = nBitsTotal;
|
|
||||||
dataWords[(nBitsLeft + 64 >>> 9 << 4) + 15] = (nBitsTotalH << 8 | nBitsTotalH >>> 24) & 16711935 | (nBitsTotalH << 24 | nBitsTotalH >>> 8) & 4278255360;
|
|
||||||
dataWords[(nBitsLeft + 64 >>> 9 << 4) + 14] = (nBitsTotalL << 8 | nBitsTotalL >>> 24) & 16711935 | (nBitsTotalL << 24 | nBitsTotalL >>> 8) & 4278255360;
|
|
||||||
data.sigBytes = (dataWords.length + 1) * 4;
|
|
||||||
this._process();
|
|
||||||
const hash = this._hash;
|
|
||||||
const H = hash.words;
|
|
||||||
for (let i = 0; i < 4; i += 1) {
|
|
||||||
const H_i = H[i];
|
|
||||||
H[i] = (H_i << 8 | H_i >>> 24) & 16711935 | (H_i << 24 | H_i >>> 8) & 4278255360;
|
|
||||||
}
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
clone() {
|
|
||||||
const clone = super.clone.call(this);
|
|
||||||
clone._hash = this._hash.clone();
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Shortcut function to the hasher's object interface.
|
|
||||||
*
|
|
||||||
* @param message - The message to hash.
|
|
||||||
* @returns The hash.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```js
|
|
||||||
* const hash = MD5('message');
|
|
||||||
* const hash = MD5(wordArray);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
const MD5 = Hasher._createHelper(MD5Algo);
|
|
||||||
/**
|
|
||||||
* Shortcut function to the HMAC's object interface.
|
|
||||||
*
|
|
||||||
* @param message - The message to hash.
|
|
||||||
* @param key - The secret key.
|
|
||||||
* @returns The HMAC.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```js
|
|
||||||
* const hmac = HmacMD5(message, key);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
const HmacMD5 = Hasher._createHmacHelper(MD5Algo);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { HmacMD5, MD5, MD5Algo };
|
|
||||||
//# sourceMappingURL=md5.mjs.map
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
import { BlockCipherMode } from "./cipher-core.mjs";
|
|
||||||
|
|
||||||
//#region src/mode-cfb.ts
|
|
||||||
function generateKeystreamAndEncrypt(words, offset, blockSize, cipher) {
|
|
||||||
const _words = words;
|
|
||||||
let keystream;
|
|
||||||
const iv = this._iv;
|
|
||||||
if (iv) {
|
|
||||||
keystream = iv.slice(0);
|
|
||||||
this._iv = void 0;
|
|
||||||
} else keystream = this._prevBlock;
|
|
||||||
cipher.encryptBlock(keystream, 0);
|
|
||||||
for (let i = 0; i < blockSize; i += 1) _words[offset + i] ^= keystream[i];
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* CFB Encryptor
|
|
||||||
*/
|
|
||||||
var CFBEncryptor = class extends BlockCipherMode {
|
|
||||||
processBlock(words, offset) {
|
|
||||||
const cipher = this._cipher;
|
|
||||||
const blockSize = cipher.blockSize;
|
|
||||||
generateKeystreamAndEncrypt.call(this, words, offset, blockSize, cipher);
|
|
||||||
this._prevBlock = words.slice(offset, offset + blockSize);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* CFB Decryptor
|
|
||||||
*/
|
|
||||||
var CFBDecryptor = class extends BlockCipherMode {
|
|
||||||
processBlock(words, offset) {
|
|
||||||
const cipher = this._cipher;
|
|
||||||
const blockSize = cipher.blockSize;
|
|
||||||
const thisBlock = words.slice(offset, offset + blockSize);
|
|
||||||
generateKeystreamAndEncrypt.call(this, words, offset, blockSize, cipher);
|
|
||||||
this._prevBlock = thisBlock;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Cipher Feedback block mode.
|
|
||||||
*/
|
|
||||||
var CFB = class extends BlockCipherMode {
|
|
||||||
static Encryptor = CFBEncryptor;
|
|
||||||
static Decryptor = CFBDecryptor;
|
|
||||||
};
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { CFB };
|
|
||||||
//# sourceMappingURL=mode-cfb.mjs.map
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
import { BlockCipherMode } from "./cipher-core.mjs";
|
|
||||||
|
|
||||||
//#region src/mode-ctr-gladman.ts
|
|
||||||
const incWord = (word) => {
|
|
||||||
let _word = word;
|
|
||||||
if ((word >> 24 & 255) === 255) {
|
|
||||||
let b1 = word >> 16 & 255;
|
|
||||||
let b2 = word >> 8 & 255;
|
|
||||||
let b3 = word & 255;
|
|
||||||
if (b1 === 255) {
|
|
||||||
b1 = 0;
|
|
||||||
if (b2 === 255) {
|
|
||||||
b2 = 0;
|
|
||||||
if (b3 === 255) b3 = 0;
|
|
||||||
else b3 += 1;
|
|
||||||
} else b2 += 1;
|
|
||||||
} else b1 += 1;
|
|
||||||
_word = 0;
|
|
||||||
_word += b1 << 16;
|
|
||||||
_word += b2 << 8;
|
|
||||||
_word += b3;
|
|
||||||
} else _word += 1 << 24;
|
|
||||||
return _word;
|
|
||||||
};
|
|
||||||
const incCounter = (counter) => {
|
|
||||||
const _counter = counter;
|
|
||||||
_counter[0] = incWord(_counter[0]);
|
|
||||||
if (_counter[0] === 0) _counter[1] = incWord(_counter[1]);
|
|
||||||
return _counter;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* CTRGladman Encryptor/Decryptor (same operation)
|
|
||||||
*/
|
|
||||||
var CTRGladmanMode = class extends BlockCipherMode {
|
|
||||||
/** Counter for CTR Gladman mode */
|
|
||||||
_counter;
|
|
||||||
processBlock(words, offset) {
|
|
||||||
const _words = words;
|
|
||||||
const cipher = this._cipher;
|
|
||||||
const blockSize = cipher.blockSize;
|
|
||||||
const iv = this._iv;
|
|
||||||
let counter = this._counter;
|
|
||||||
if (iv) {
|
|
||||||
this._counter = iv.slice(0);
|
|
||||||
counter = this._counter;
|
|
||||||
this._iv = void 0;
|
|
||||||
}
|
|
||||||
incCounter(counter);
|
|
||||||
const keystream = counter.slice(0);
|
|
||||||
cipher.encryptBlock(keystream, 0);
|
|
||||||
for (let i = 0; i < blockSize; i += 1) _words[offset + i] ^= keystream[i];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/** @preserve
|
|
||||||
* Counter block mode compatible with Dr Brian Gladman fileenc.c
|
|
||||||
* derived from CTR mode
|
|
||||||
* Jan Hruby jhruby.web@gmail.com
|
|
||||||
*/
|
|
||||||
var CTRGladman = class extends BlockCipherMode {
|
|
||||||
/** Counter for CTR Gladman mode */
|
|
||||||
_counter;
|
|
||||||
static Encryptor = CTRGladmanMode;
|
|
||||||
static Decryptor = CTRGladmanMode;
|
|
||||||
};
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { CTRGladman };
|
|
||||||
//# sourceMappingURL=mode-ctr-gladman.mjs.map
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import { BlockCipherMode } from "./cipher-core.mjs";
|
|
||||||
|
|
||||||
//#region src/mode-ctr.ts
|
|
||||||
/**
|
|
||||||
* CTR Encryptor/Decryptor (same operation)
|
|
||||||
*/
|
|
||||||
var CTRMode = class extends BlockCipherMode {
|
|
||||||
/** Counter for CTR mode */
|
|
||||||
_counter;
|
|
||||||
processBlock(words, offset) {
|
|
||||||
const _words = words;
|
|
||||||
const cipher = this._cipher;
|
|
||||||
const blockSize = cipher.blockSize;
|
|
||||||
const iv = this._iv;
|
|
||||||
let counter = this._counter;
|
|
||||||
if (iv) {
|
|
||||||
this._counter = iv.slice(0);
|
|
||||||
counter = this._counter;
|
|
||||||
this._iv = void 0;
|
|
||||||
}
|
|
||||||
const keystream = counter.slice(0);
|
|
||||||
cipher.encryptBlock(keystream, 0);
|
|
||||||
counter[blockSize - 1] = counter[blockSize - 1] + 1 | 0;
|
|
||||||
for (let i = 0; i < blockSize; i += 1) _words[offset + i] ^= keystream[i];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Counter block mode.
|
|
||||||
*/
|
|
||||||
var CTR = class extends BlockCipherMode {
|
|
||||||
/** Counter for CTR mode */
|
|
||||||
_counter;
|
|
||||||
static Encryptor = CTRMode;
|
|
||||||
static Decryptor = CTRMode;
|
|
||||||
};
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { CTR };
|
|
||||||
//# sourceMappingURL=mode-ctr.mjs.map
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import { BlockCipherMode } from "./cipher-core.mjs";
|
|
||||||
|
|
||||||
//#region src/mode-ecb.ts
|
|
||||||
/**
|
|
||||||
* ECB Encryptor
|
|
||||||
*/
|
|
||||||
var ECBEncryptor = class extends BlockCipherMode {
|
|
||||||
processBlock(words, offset) {
|
|
||||||
this._cipher.encryptBlock(words, offset);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* ECB Decryptor
|
|
||||||
*/
|
|
||||||
var ECBDecryptor = class extends BlockCipherMode {
|
|
||||||
processBlock(words, offset) {
|
|
||||||
this._cipher.decryptBlock(words, offset);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Electronic Codebook block mode.
|
|
||||||
*/
|
|
||||||
var ECB = class extends BlockCipherMode {
|
|
||||||
static Encryptor = ECBEncryptor;
|
|
||||||
static Decryptor = ECBDecryptor;
|
|
||||||
};
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { ECB };
|
|
||||||
//# sourceMappingURL=mode-ecb.mjs.map
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import { BlockCipherMode } from "./cipher-core.mjs";
|
|
||||||
|
|
||||||
//#region src/mode-ofb.ts
|
|
||||||
/**
|
|
||||||
* OFB Encryptor/Decryptor (same operation)
|
|
||||||
*/
|
|
||||||
var OFBMode = class extends BlockCipherMode {
|
|
||||||
/** Keystream for OFB mode */
|
|
||||||
_keystream;
|
|
||||||
processBlock(words, offset) {
|
|
||||||
const _words = words;
|
|
||||||
const cipher = this._cipher;
|
|
||||||
const blockSize = cipher.blockSize;
|
|
||||||
const iv = this._iv;
|
|
||||||
let keystream = this._keystream;
|
|
||||||
if (iv) {
|
|
||||||
this._keystream = iv.slice(0);
|
|
||||||
keystream = this._keystream;
|
|
||||||
this._iv = void 0;
|
|
||||||
} else if (!keystream) {
|
|
||||||
this._keystream = new Array(blockSize).fill(0);
|
|
||||||
keystream = this._keystream;
|
|
||||||
}
|
|
||||||
cipher.encryptBlock(keystream, 0);
|
|
||||||
for (let i = 0; i < blockSize; i += 1) _words[offset + i] ^= keystream[i];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Output Feedback block mode.
|
|
||||||
*/
|
|
||||||
var OFB = class extends BlockCipherMode {
|
|
||||||
/** Keystream for OFB mode */
|
|
||||||
_keystream;
|
|
||||||
static Encryptor = OFBMode;
|
|
||||||
static Decryptor = OFBMode;
|
|
||||||
};
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { OFB };
|
|
||||||
//# sourceMappingURL=mode-ofb.mjs.map
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
//#region src/pad-ansix923.ts
|
|
||||||
/**
|
|
||||||
* ANSI X.923 padding strategy.
|
|
||||||
*/
|
|
||||||
const AnsiX923 = {
|
|
||||||
pad(data, blockSize) {
|
|
||||||
const _data = data;
|
|
||||||
const dataSigBytes = _data.sigBytes;
|
|
||||||
const blockSizeBytes = blockSize * 4;
|
|
||||||
const nPaddingBytes = blockSizeBytes - dataSigBytes % blockSizeBytes;
|
|
||||||
const lastBytePos = dataSigBytes + nPaddingBytes - 1;
|
|
||||||
_data.clamp();
|
|
||||||
_data.words[lastBytePos >>> 2] |= nPaddingBytes << 24 - lastBytePos % 4 * 8;
|
|
||||||
_data.sigBytes += nPaddingBytes;
|
|
||||||
},
|
|
||||||
unpad(data) {
|
|
||||||
const _data = data;
|
|
||||||
const nPaddingBytes = _data.words[_data.sigBytes - 1 >>> 2] & 255;
|
|
||||||
_data.sigBytes -= nPaddingBytes;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { AnsiX923 };
|
|
||||||
//# sourceMappingURL=pad-ansix923.mjs.map
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { WordArray } from "./core.mjs";
|
|
||||||
|
|
||||||
//#region src/pad-iso10126.ts
|
|
||||||
/**
|
|
||||||
* ISO 10126 padding strategy.
|
|
||||||
*/
|
|
||||||
const Iso10126 = {
|
|
||||||
pad(data, blockSize) {
|
|
||||||
const blockSizeBytes = blockSize * 4;
|
|
||||||
const nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes;
|
|
||||||
data.concat(WordArray.random(nPaddingBytes - 1)).concat(WordArray.create([nPaddingBytes << 24], 1));
|
|
||||||
},
|
|
||||||
unpad(data) {
|
|
||||||
const _data = data;
|
|
||||||
const nPaddingBytes = _data.words[_data.sigBytes - 1 >>> 2] & 255;
|
|
||||||
_data.sigBytes -= nPaddingBytes;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { Iso10126 };
|
|
||||||
//# sourceMappingURL=pad-iso10126.mjs.map
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { WordArray } from "./core.mjs";
|
|
||||||
import { ZeroPadding } from "./pad-zeropadding.mjs";
|
|
||||||
|
|
||||||
//#region src/pad-iso97971.ts
|
|
||||||
/**
|
|
||||||
* ISO/IEC 9797-1 Padding Method 2.
|
|
||||||
*/
|
|
||||||
const Iso97971 = {
|
|
||||||
pad(data, blockSize) {
|
|
||||||
data.concat(WordArray.create([2147483648], 1));
|
|
||||||
ZeroPadding.pad(data, blockSize);
|
|
||||||
},
|
|
||||||
unpad(data) {
|
|
||||||
const _data = data;
|
|
||||||
ZeroPadding.unpad(_data);
|
|
||||||
_data.sigBytes -= 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { Iso97971 };
|
|
||||||
//# sourceMappingURL=pad-iso97971.mjs.map
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
//#region src/pad-nopadding.ts
|
|
||||||
/**
|
|
||||||
* A noop padding strategy.
|
|
||||||
*/
|
|
||||||
const NoPadding = {
|
|
||||||
pad(_data, _blockSize) {},
|
|
||||||
unpad(_data) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { NoPadding };
|
|
||||||
//# sourceMappingURL=pad-nopadding.mjs.map
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
//#region src/pad-zeropadding.ts
|
|
||||||
/**
|
|
||||||
* Zero padding strategy.
|
|
||||||
*/
|
|
||||||
const ZeroPadding = {
|
|
||||||
pad(data, blockSize) {
|
|
||||||
const _data = data;
|
|
||||||
const blockSizeBytes = blockSize * 4;
|
|
||||||
_data.clamp();
|
|
||||||
_data.sigBytes += blockSizeBytes - (data.sigBytes % blockSizeBytes || blockSizeBytes);
|
|
||||||
},
|
|
||||||
unpad(data) {
|
|
||||||
const _data = data;
|
|
||||||
const dataWords = _data.words;
|
|
||||||
for (let i = _data.sigBytes - 1; i >= 0; i -= 1) if (dataWords[i >>> 2] >>> 24 - i % 4 * 8 & 255) {
|
|
||||||
_data.sigBytes = i + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { ZeroPadding };
|
|
||||||
//# sourceMappingURL=pad-zeropadding.mjs.map
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
import { Base, HMAC, WordArray } from "./core.mjs";
|
|
||||||
import { SHA256Algo } from "./sha256.mjs";
|
|
||||||
|
|
||||||
//#region src/pbkdf2.ts
|
|
||||||
/**
|
|
||||||
* Password-Based Key Derivation Function 2 algorithm.
|
|
||||||
*/
|
|
||||||
var PBKDF2Algo = class extends Base {
|
|
||||||
cfg;
|
|
||||||
/**
|
|
||||||
* Initializes a newly created key derivation function.
|
|
||||||
*
|
|
||||||
* @param {Object} cfg (Optional) The configuration options to use for the derivation.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* const kdf = new PBKDF2Algo();
|
|
||||||
* const kdf = new PBKDF2Algo({ keySize: 8 });
|
|
||||||
* const kdf = new PBKDF2Algo({ keySize: 8, iterations: 1000 });
|
|
||||||
*/
|
|
||||||
constructor(cfg) {
|
|
||||||
super();
|
|
||||||
/**
|
|
||||||
* Configuration options.
|
|
||||||
*
|
|
||||||
* The default `hasher` and `interations` is different from CryptoJs to enhance security:
|
|
||||||
* https://github.com/entronad/crypto-es/security/advisories/GHSA-mpj8-q39x-wq5h
|
|
||||||
*
|
|
||||||
* @property {number} keySize The key size in words to generate. Default: 4 (128 bits)
|
|
||||||
* @property {Hasher} hasher The hasher to use. Default: SHA256
|
|
||||||
* @property {number} iterations The number of iterations to perform. Default: 250000
|
|
||||||
*/
|
|
||||||
this.cfg = Object.assign({}, {
|
|
||||||
keySize: 128 / 32,
|
|
||||||
hasher: SHA256Algo,
|
|
||||||
iterations: 25e4
|
|
||||||
}, cfg);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Computes the Password-Based Key Derivation Function 2.
|
|
||||||
*
|
|
||||||
* @param {WordArray|string} password The password.
|
|
||||||
* @param {WordArray|string} salt A salt.
|
|
||||||
*
|
|
||||||
* @return {WordArray} The derived key.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* const key = kdf.compute(password, salt);
|
|
||||||
*/
|
|
||||||
compute(password, salt) {
|
|
||||||
const { cfg } = this;
|
|
||||||
const hmac = HMAC.create(cfg.hasher, password);
|
|
||||||
const derivedKey = WordArray.create();
|
|
||||||
const blockIndex = WordArray.create([1]);
|
|
||||||
const derivedKeyWords = derivedKey.words;
|
|
||||||
const blockIndexWords = blockIndex.words;
|
|
||||||
const { keySize, iterations } = cfg;
|
|
||||||
while (derivedKeyWords.length < keySize) {
|
|
||||||
const block = hmac.update(salt).finalize(blockIndex);
|
|
||||||
hmac.reset();
|
|
||||||
const blockWords = block.words;
|
|
||||||
const blockWordsLength = blockWords.length;
|
|
||||||
let intermediate = block;
|
|
||||||
for (let i = 1; i < iterations; i += 1) {
|
|
||||||
intermediate = hmac.finalize(intermediate);
|
|
||||||
hmac.reset();
|
|
||||||
const intermediateWords = intermediate.words;
|
|
||||||
for (let j = 0; j < blockWordsLength; j += 1) blockWords[j] ^= intermediateWords[j];
|
|
||||||
}
|
|
||||||
derivedKey.concat(block);
|
|
||||||
blockIndexWords[0] += 1;
|
|
||||||
}
|
|
||||||
derivedKey.sigBytes = keySize * 4;
|
|
||||||
return derivedKey;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Computes the Password-Based Key Derivation Function 2.
|
|
||||||
*
|
|
||||||
* @param {WordArray|string} password The password.
|
|
||||||
* @param {WordArray|string} salt A salt.
|
|
||||||
* @param {Object} cfg (Optional) The configuration options to use for this computation.
|
|
||||||
*
|
|
||||||
* @return {WordArray} The derived key.
|
|
||||||
*
|
|
||||||
* @static
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* var key = PBKDF2(password, salt);
|
|
||||||
* var key = PBKDF2(password, salt, { keySize: 8 });
|
|
||||||
* var key = PBKDF2(password, salt, { keySize: 8, iterations: 1000 });
|
|
||||||
*/
|
|
||||||
const PBKDF2 = (password, salt, cfg) => new PBKDF2Algo(cfg).compute(password, salt);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { PBKDF2, PBKDF2Algo };
|
|
||||||
//# sourceMappingURL=pbkdf2.mjs.map
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
import { StreamCipher } from "./cipher-core.mjs";
|
|
||||||
|
|
||||||
//#region src/rabbit-legacy.ts
|
|
||||||
const S = [];
|
|
||||||
const C_ = [];
|
|
||||||
const G = [];
|
|
||||||
function nextState() {
|
|
||||||
const X = this._X;
|
|
||||||
const C = this._C;
|
|
||||||
for (let i = 0; i < 8; i += 1) C_[i] = C[i];
|
|
||||||
C[0] = C[0] + 1295307597 + this._b | 0;
|
|
||||||
C[1] = C[1] + 3545052371 + (C[0] >>> 0 < C_[0] >>> 0 ? 1 : 0) | 0;
|
|
||||||
C[2] = C[2] + 886263092 + (C[1] >>> 0 < C_[1] >>> 0 ? 1 : 0) | 0;
|
|
||||||
C[3] = C[3] + 1295307597 + (C[2] >>> 0 < C_[2] >>> 0 ? 1 : 0) | 0;
|
|
||||||
C[4] = C[4] + 3545052371 + (C[3] >>> 0 < C_[3] >>> 0 ? 1 : 0) | 0;
|
|
||||||
C[5] = C[5] + 886263092 + (C[4] >>> 0 < C_[4] >>> 0 ? 1 : 0) | 0;
|
|
||||||
C[6] = C[6] + 1295307597 + (C[5] >>> 0 < C_[5] >>> 0 ? 1 : 0) | 0;
|
|
||||||
C[7] = C[7] + 3545052371 + (C[6] >>> 0 < C_[6] >>> 0 ? 1 : 0) | 0;
|
|
||||||
this._b = C[7] >>> 0 < C_[7] >>> 0 ? 1 : 0;
|
|
||||||
for (let i = 0; i < 8; i += 1) {
|
|
||||||
const gx = X[i] + C[i];
|
|
||||||
const ga = gx & 65535;
|
|
||||||
const gb = gx >>> 16;
|
|
||||||
const gh = ((ga * ga >>> 17) + ga * gb >>> 15) + gb * gb;
|
|
||||||
const gl = ((gx & 4294901760) * gx | 0) + ((gx & 65535) * gx | 0);
|
|
||||||
G[i] = gh ^ gl;
|
|
||||||
}
|
|
||||||
X[0] = G[0] + (G[7] << 16 | G[7] >>> 16) + (G[6] << 16 | G[6] >>> 16) | 0;
|
|
||||||
X[1] = G[1] + (G[0] << 8 | G[0] >>> 24) + G[7] | 0;
|
|
||||||
X[2] = G[2] + (G[1] << 16 | G[1] >>> 16) + (G[0] << 16 | G[0] >>> 16) | 0;
|
|
||||||
X[3] = G[3] + (G[2] << 8 | G[2] >>> 24) + G[1] | 0;
|
|
||||||
X[4] = G[4] + (G[3] << 16 | G[3] >>> 16) + (G[2] << 16 | G[2] >>> 16) | 0;
|
|
||||||
X[5] = G[5] + (G[4] << 8 | G[4] >>> 24) + G[3] | 0;
|
|
||||||
X[6] = G[6] + (G[5] << 16 | G[5] >>> 16) + (G[4] << 16 | G[4] >>> 16) | 0;
|
|
||||||
X[7] = G[7] + (G[6] << 8 | G[6] >>> 24) + G[5] | 0;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Rabbit stream cipher algorithm.
|
|
||||||
*
|
|
||||||
* This is a legacy version that neglected to convert the key to little-endian.
|
|
||||||
* This error doesn't affect the cipher's security,
|
|
||||||
* but it does affect its compatibility with other implementations.
|
|
||||||
*/
|
|
||||||
var RabbitLegacyAlgo = class extends StreamCipher {
|
|
||||||
_X;
|
|
||||||
_C;
|
|
||||||
_b;
|
|
||||||
static ivSize = 64 / 32;
|
|
||||||
constructor(xformMode, key, cfg) {
|
|
||||||
super(xformMode, key, cfg);
|
|
||||||
this.blockSize = 128 / 32;
|
|
||||||
}
|
|
||||||
_doReset() {
|
|
||||||
const K = this._key.words;
|
|
||||||
const { iv } = this.cfg;
|
|
||||||
this._X = [
|
|
||||||
K[0],
|
|
||||||
K[3] << 16 | K[2] >>> 16,
|
|
||||||
K[1],
|
|
||||||
K[0] << 16 | K[3] >>> 16,
|
|
||||||
K[2],
|
|
||||||
K[1] << 16 | K[0] >>> 16,
|
|
||||||
K[3],
|
|
||||||
K[2] << 16 | K[1] >>> 16
|
|
||||||
];
|
|
||||||
const X = this._X;
|
|
||||||
this._C = [
|
|
||||||
K[2] << 16 | K[2] >>> 16,
|
|
||||||
K[0] & 4294901760 | K[1] & 65535,
|
|
||||||
K[3] << 16 | K[3] >>> 16,
|
|
||||||
K[1] & 4294901760 | K[2] & 65535,
|
|
||||||
K[0] << 16 | K[0] >>> 16,
|
|
||||||
K[2] & 4294901760 | K[3] & 65535,
|
|
||||||
K[1] << 16 | K[1] >>> 16,
|
|
||||||
K[3] & 4294901760 | K[0] & 65535
|
|
||||||
];
|
|
||||||
const C = this._C;
|
|
||||||
this._b = 0;
|
|
||||||
for (let i = 0; i < 4; i += 1) nextState.call(this);
|
|
||||||
for (let i = 0; i < 8; i += 1) C[i] ^= X[i + 4 & 7];
|
|
||||||
if (iv) {
|
|
||||||
const IV = iv.words;
|
|
||||||
const IV_0 = IV[0];
|
|
||||||
const IV_1 = IV[1];
|
|
||||||
const i0 = (IV_0 << 8 | IV_0 >>> 24) & 16711935 | (IV_0 << 24 | IV_0 >>> 8) & 4278255360;
|
|
||||||
const i2 = (IV_1 << 8 | IV_1 >>> 24) & 16711935 | (IV_1 << 24 | IV_1 >>> 8) & 4278255360;
|
|
||||||
const i1 = i0 >>> 16 | i2 & 4294901760;
|
|
||||||
const i3 = i2 << 16 | i0 & 65535;
|
|
||||||
C[0] ^= i0;
|
|
||||||
C[1] ^= i1;
|
|
||||||
C[2] ^= i2;
|
|
||||||
C[3] ^= i3;
|
|
||||||
C[4] ^= i0;
|
|
||||||
C[5] ^= i1;
|
|
||||||
C[6] ^= i2;
|
|
||||||
C[7] ^= i3;
|
|
||||||
for (let i = 0; i < 4; i += 1) nextState.call(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_doProcessBlock(M, offset) {
|
|
||||||
const _M = M;
|
|
||||||
const X = this._X;
|
|
||||||
nextState.call(this);
|
|
||||||
S[0] = X[0] ^ X[5] >>> 16 ^ X[3] << 16;
|
|
||||||
S[1] = X[2] ^ X[7] >>> 16 ^ X[5] << 16;
|
|
||||||
S[2] = X[4] ^ X[1] >>> 16 ^ X[7] << 16;
|
|
||||||
S[3] = X[6] ^ X[3] >>> 16 ^ X[1] << 16;
|
|
||||||
for (let i = 0; i < 4; i += 1) {
|
|
||||||
S[i] = (S[i] << 8 | S[i] >>> 24) & 16711935 | (S[i] << 24 | S[i] >>> 8) & 4278255360;
|
|
||||||
_M[offset + i] ^= S[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Shortcut functions to the cipher's object interface.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* var ciphertext = RabbitLegacy.encrypt(message, key, cfg);
|
|
||||||
* var plaintext = RabbitLegacy.decrypt(ciphertext, key, cfg);
|
|
||||||
*/
|
|
||||||
const RabbitLegacy = StreamCipher._createHelper(RabbitLegacyAlgo);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { RabbitLegacy, RabbitLegacyAlgo };
|
|
||||||
//# sourceMappingURL=rabbit-legacy.mjs.map
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
import { StreamCipher } from "./cipher-core.mjs";
|
|
||||||
|
|
||||||
//#region src/rabbit.ts
|
|
||||||
const S = [];
|
|
||||||
const C_ = [];
|
|
||||||
const G = [];
|
|
||||||
function nextState() {
|
|
||||||
const X = this._X;
|
|
||||||
const C = this._C;
|
|
||||||
for (let i = 0; i < 8; i += 1) C_[i] = C[i];
|
|
||||||
C[0] = C[0] + 1295307597 + this._b | 0;
|
|
||||||
C[1] = C[1] + 3545052371 + (C[0] >>> 0 < C_[0] >>> 0 ? 1 : 0) | 0;
|
|
||||||
C[2] = C[2] + 886263092 + (C[1] >>> 0 < C_[1] >>> 0 ? 1 : 0) | 0;
|
|
||||||
C[3] = C[3] + 1295307597 + (C[2] >>> 0 < C_[2] >>> 0 ? 1 : 0) | 0;
|
|
||||||
C[4] = C[4] + 3545052371 + (C[3] >>> 0 < C_[3] >>> 0 ? 1 : 0) | 0;
|
|
||||||
C[5] = C[5] + 886263092 + (C[4] >>> 0 < C_[4] >>> 0 ? 1 : 0) | 0;
|
|
||||||
C[6] = C[6] + 1295307597 + (C[5] >>> 0 < C_[5] >>> 0 ? 1 : 0) | 0;
|
|
||||||
C[7] = C[7] + 3545052371 + (C[6] >>> 0 < C_[6] >>> 0 ? 1 : 0) | 0;
|
|
||||||
this._b = C[7] >>> 0 < C_[7] >>> 0 ? 1 : 0;
|
|
||||||
for (let i = 0; i < 8; i += 1) {
|
|
||||||
const gx = X[i] + C[i];
|
|
||||||
const ga = gx & 65535;
|
|
||||||
const gb = gx >>> 16;
|
|
||||||
const gh = ((ga * ga >>> 17) + ga * gb >>> 15) + gb * gb;
|
|
||||||
const gl = ((gx & 4294901760) * gx | 0) + ((gx & 65535) * gx | 0);
|
|
||||||
G[i] = gh ^ gl;
|
|
||||||
}
|
|
||||||
X[0] = G[0] + (G[7] << 16 | G[7] >>> 16) + (G[6] << 16 | G[6] >>> 16) | 0;
|
|
||||||
X[1] = G[1] + (G[0] << 8 | G[0] >>> 24) + G[7] | 0;
|
|
||||||
X[2] = G[2] + (G[1] << 16 | G[1] >>> 16) + (G[0] << 16 | G[0] >>> 16) | 0;
|
|
||||||
X[3] = G[3] + (G[2] << 8 | G[2] >>> 24) + G[1] | 0;
|
|
||||||
X[4] = G[4] + (G[3] << 16 | G[3] >>> 16) + (G[2] << 16 | G[2] >>> 16) | 0;
|
|
||||||
X[5] = G[5] + (G[4] << 8 | G[4] >>> 24) + G[3] | 0;
|
|
||||||
X[6] = G[6] + (G[5] << 16 | G[5] >>> 16) + (G[4] << 16 | G[4] >>> 16) | 0;
|
|
||||||
X[7] = G[7] + (G[6] << 8 | G[6] >>> 24) + G[5] | 0;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Rabbit stream cipher algorithm
|
|
||||||
*/
|
|
||||||
var RabbitAlgo = class extends StreamCipher {
|
|
||||||
_X;
|
|
||||||
_C;
|
|
||||||
_b;
|
|
||||||
static ivSize = 64 / 32;
|
|
||||||
constructor(xformMode, key, cfg) {
|
|
||||||
super(xformMode, key, cfg);
|
|
||||||
this.blockSize = 128 / 32;
|
|
||||||
}
|
|
||||||
_doReset() {
|
|
||||||
const K = this._key.words;
|
|
||||||
const { iv } = this.cfg;
|
|
||||||
for (let i = 0; i < 4; i += 1) K[i] = (K[i] << 8 | K[i] >>> 24) & 16711935 | (K[i] << 24 | K[i] >>> 8) & 4278255360;
|
|
||||||
this._X = [
|
|
||||||
K[0],
|
|
||||||
K[3] << 16 | K[2] >>> 16,
|
|
||||||
K[1],
|
|
||||||
K[0] << 16 | K[3] >>> 16,
|
|
||||||
K[2],
|
|
||||||
K[1] << 16 | K[0] >>> 16,
|
|
||||||
K[3],
|
|
||||||
K[2] << 16 | K[1] >>> 16
|
|
||||||
];
|
|
||||||
const X = this._X;
|
|
||||||
this._C = [
|
|
||||||
K[2] << 16 | K[2] >>> 16,
|
|
||||||
K[0] & 4294901760 | K[1] & 65535,
|
|
||||||
K[3] << 16 | K[3] >>> 16,
|
|
||||||
K[1] & 4294901760 | K[2] & 65535,
|
|
||||||
K[0] << 16 | K[0] >>> 16,
|
|
||||||
K[2] & 4294901760 | K[3] & 65535,
|
|
||||||
K[1] << 16 | K[1] >>> 16,
|
|
||||||
K[3] & 4294901760 | K[0] & 65535
|
|
||||||
];
|
|
||||||
const C = this._C;
|
|
||||||
this._b = 0;
|
|
||||||
for (let i = 0; i < 4; i += 1) nextState.call(this);
|
|
||||||
for (let i = 0; i < 8; i += 1) C[i] ^= X[i + 4 & 7];
|
|
||||||
if (iv) {
|
|
||||||
const IV = iv.words;
|
|
||||||
const IV_0 = IV[0];
|
|
||||||
const IV_1 = IV[1];
|
|
||||||
const i0 = (IV_0 << 8 | IV_0 >>> 24) & 16711935 | (IV_0 << 24 | IV_0 >>> 8) & 4278255360;
|
|
||||||
const i2 = (IV_1 << 8 | IV_1 >>> 24) & 16711935 | (IV_1 << 24 | IV_1 >>> 8) & 4278255360;
|
|
||||||
const i1 = i0 >>> 16 | i2 & 4294901760;
|
|
||||||
const i3 = i2 << 16 | i0 & 65535;
|
|
||||||
C[0] ^= i0;
|
|
||||||
C[1] ^= i1;
|
|
||||||
C[2] ^= i2;
|
|
||||||
C[3] ^= i3;
|
|
||||||
C[4] ^= i0;
|
|
||||||
C[5] ^= i1;
|
|
||||||
C[6] ^= i2;
|
|
||||||
C[7] ^= i3;
|
|
||||||
for (let i = 0; i < 4; i += 1) nextState.call(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_doProcessBlock(M, offset) {
|
|
||||||
const _M = M;
|
|
||||||
const X = this._X;
|
|
||||||
nextState.call(this);
|
|
||||||
S[0] = X[0] ^ X[5] >>> 16 ^ X[3] << 16;
|
|
||||||
S[1] = X[2] ^ X[7] >>> 16 ^ X[5] << 16;
|
|
||||||
S[2] = X[4] ^ X[1] >>> 16 ^ X[7] << 16;
|
|
||||||
S[3] = X[6] ^ X[3] >>> 16 ^ X[1] << 16;
|
|
||||||
for (let i = 0; i < 4; i += 1) {
|
|
||||||
S[i] = (S[i] << 8 | S[i] >>> 24) & 16711935 | (S[i] << 24 | S[i] >>> 8) & 4278255360;
|
|
||||||
_M[offset + i] ^= S[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Shortcut functions to the cipher's object interface.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* var ciphertext = Rabbit.encrypt(message, key, cfg);
|
|
||||||
* var plaintext = Rabbit.decrypt(ciphertext, key, cfg);
|
|
||||||
*/
|
|
||||||
const Rabbit = StreamCipher._createHelper(RabbitAlgo);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { Rabbit, RabbitAlgo };
|
|
||||||
//# sourceMappingURL=rabbit.mjs.map
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
import { StreamCipher } from "./cipher-core.mjs";
|
|
||||||
|
|
||||||
//#region src/rc4.ts
|
|
||||||
/**
|
|
||||||
* RC4 stream cipher algorithm.
|
|
||||||
*/
|
|
||||||
var RC4Algo = class extends StreamCipher {
|
|
||||||
static keySize = 256 / 32;
|
|
||||||
static ivSize = 0;
|
|
||||||
_S;
|
|
||||||
_i;
|
|
||||||
_j;
|
|
||||||
generateKeystreamWord() {
|
|
||||||
const S = this._S;
|
|
||||||
let i = this._i;
|
|
||||||
let j = this._j;
|
|
||||||
let keystreamWord = 0;
|
|
||||||
for (let n = 0; n < 4; n += 1) {
|
|
||||||
i = (i + 1) % 256;
|
|
||||||
j = (j + S[i]) % 256;
|
|
||||||
const t = S[i];
|
|
||||||
S[i] = S[j];
|
|
||||||
S[j] = t;
|
|
||||||
keystreamWord |= S[(S[i] + S[j]) % 256] << 24 - n * 8;
|
|
||||||
}
|
|
||||||
this._i = i;
|
|
||||||
this._j = j;
|
|
||||||
return keystreamWord;
|
|
||||||
}
|
|
||||||
_doReset() {
|
|
||||||
const key = this._key;
|
|
||||||
const keyWords = key.words;
|
|
||||||
const keySigBytes = key.sigBytes;
|
|
||||||
this._S = [];
|
|
||||||
const S = this._S;
|
|
||||||
for (let i = 0; i < 256; i += 1) S[i] = i;
|
|
||||||
for (let i = 0, j = 0; i < 256; i += 1) {
|
|
||||||
const keyByteIndex = i % keySigBytes;
|
|
||||||
const keyByte = keyWords[keyByteIndex >>> 2] >>> 24 - keyByteIndex % 4 * 8 & 255;
|
|
||||||
j = (j + S[i] + keyByte) % 256;
|
|
||||||
const t = S[i];
|
|
||||||
S[i] = S[j];
|
|
||||||
S[j] = t;
|
|
||||||
}
|
|
||||||
this._j = 0;
|
|
||||||
this._i = this._j;
|
|
||||||
}
|
|
||||||
_doProcessBlock(M, offset) {
|
|
||||||
const _M = M;
|
|
||||||
_M[offset] ^= this.generateKeystreamWord();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Shortcut functions to the cipher's object interface.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* var ciphertext = RC4.encrypt(message, key, cfg);
|
|
||||||
* var plaintext = RC4.decrypt(ciphertext, key, cfg);
|
|
||||||
*/
|
|
||||||
const RC4 = StreamCipher._createHelper(RC4Algo);
|
|
||||||
/**
|
|
||||||
* Modified RC4 stream cipher algorithm.
|
|
||||||
*/
|
|
||||||
var RC4DropAlgo = class extends RC4Algo {
|
|
||||||
constructor(xformMode, key, cfg) {
|
|
||||||
super(xformMode, key, cfg);
|
|
||||||
/**
|
|
||||||
* Configuration options.
|
|
||||||
*
|
|
||||||
* @property {number} drop The number of keystream words to drop. Default 192
|
|
||||||
*/
|
|
||||||
if (this.cfg.drop === void 0) this.cfg.drop = 192;
|
|
||||||
}
|
|
||||||
_doReset() {
|
|
||||||
super._doReset();
|
|
||||||
const dropCount = this.cfg.drop || 192;
|
|
||||||
for (let i = dropCount; i > 0; i -= 1) this.generateKeystreamWord();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Shortcut functions to the cipher's object interface.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* var ciphertext = RC4Drop.encrypt(message, key, cfg);
|
|
||||||
* var plaintext = RC4Drop.decrypt(ciphertext, key, cfg);
|
|
||||||
*/
|
|
||||||
const RC4Drop = StreamCipher._createHelper(RC4DropAlgo);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { RC4, RC4Algo, RC4Drop, RC4DropAlgo };
|
|
||||||
//# sourceMappingURL=rc4.mjs.map
|
|
||||||
@@ -1,497 +0,0 @@
|
|||||||
import { Hasher, Hasher32, WordArray } from "./core.mjs";
|
|
||||||
|
|
||||||
//#region src/ripemd160.ts
|
|
||||||
const _zl = WordArray.create([
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
3,
|
|
||||||
4,
|
|
||||||
5,
|
|
||||||
6,
|
|
||||||
7,
|
|
||||||
8,
|
|
||||||
9,
|
|
||||||
10,
|
|
||||||
11,
|
|
||||||
12,
|
|
||||||
13,
|
|
||||||
14,
|
|
||||||
15,
|
|
||||||
7,
|
|
||||||
4,
|
|
||||||
13,
|
|
||||||
1,
|
|
||||||
10,
|
|
||||||
6,
|
|
||||||
15,
|
|
||||||
3,
|
|
||||||
12,
|
|
||||||
0,
|
|
||||||
9,
|
|
||||||
5,
|
|
||||||
2,
|
|
||||||
14,
|
|
||||||
11,
|
|
||||||
8,
|
|
||||||
3,
|
|
||||||
10,
|
|
||||||
14,
|
|
||||||
4,
|
|
||||||
9,
|
|
||||||
15,
|
|
||||||
8,
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
7,
|
|
||||||
0,
|
|
||||||
6,
|
|
||||||
13,
|
|
||||||
11,
|
|
||||||
5,
|
|
||||||
12,
|
|
||||||
1,
|
|
||||||
9,
|
|
||||||
11,
|
|
||||||
10,
|
|
||||||
0,
|
|
||||||
8,
|
|
||||||
12,
|
|
||||||
4,
|
|
||||||
13,
|
|
||||||
3,
|
|
||||||
7,
|
|
||||||
15,
|
|
||||||
14,
|
|
||||||
5,
|
|
||||||
6,
|
|
||||||
2,
|
|
||||||
4,
|
|
||||||
0,
|
|
||||||
5,
|
|
||||||
9,
|
|
||||||
7,
|
|
||||||
12,
|
|
||||||
2,
|
|
||||||
10,
|
|
||||||
14,
|
|
||||||
1,
|
|
||||||
3,
|
|
||||||
8,
|
|
||||||
11,
|
|
||||||
6,
|
|
||||||
15,
|
|
||||||
13
|
|
||||||
]);
|
|
||||||
const _zr = WordArray.create([
|
|
||||||
5,
|
|
||||||
14,
|
|
||||||
7,
|
|
||||||
0,
|
|
||||||
9,
|
|
||||||
2,
|
|
||||||
11,
|
|
||||||
4,
|
|
||||||
13,
|
|
||||||
6,
|
|
||||||
15,
|
|
||||||
8,
|
|
||||||
1,
|
|
||||||
10,
|
|
||||||
3,
|
|
||||||
12,
|
|
||||||
6,
|
|
||||||
11,
|
|
||||||
3,
|
|
||||||
7,
|
|
||||||
0,
|
|
||||||
13,
|
|
||||||
5,
|
|
||||||
10,
|
|
||||||
14,
|
|
||||||
15,
|
|
||||||
8,
|
|
||||||
12,
|
|
||||||
4,
|
|
||||||
9,
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
15,
|
|
||||||
5,
|
|
||||||
1,
|
|
||||||
3,
|
|
||||||
7,
|
|
||||||
14,
|
|
||||||
6,
|
|
||||||
9,
|
|
||||||
11,
|
|
||||||
8,
|
|
||||||
12,
|
|
||||||
2,
|
|
||||||
10,
|
|
||||||
0,
|
|
||||||
4,
|
|
||||||
13,
|
|
||||||
8,
|
|
||||||
6,
|
|
||||||
4,
|
|
||||||
1,
|
|
||||||
3,
|
|
||||||
11,
|
|
||||||
15,
|
|
||||||
0,
|
|
||||||
5,
|
|
||||||
12,
|
|
||||||
2,
|
|
||||||
13,
|
|
||||||
9,
|
|
||||||
7,
|
|
||||||
10,
|
|
||||||
14,
|
|
||||||
12,
|
|
||||||
15,
|
|
||||||
10,
|
|
||||||
4,
|
|
||||||
1,
|
|
||||||
5,
|
|
||||||
8,
|
|
||||||
7,
|
|
||||||
6,
|
|
||||||
2,
|
|
||||||
13,
|
|
||||||
14,
|
|
||||||
0,
|
|
||||||
3,
|
|
||||||
9,
|
|
||||||
11
|
|
||||||
]);
|
|
||||||
const _sl = WordArray.create([
|
|
||||||
11,
|
|
||||||
14,
|
|
||||||
15,
|
|
||||||
12,
|
|
||||||
5,
|
|
||||||
8,
|
|
||||||
7,
|
|
||||||
9,
|
|
||||||
11,
|
|
||||||
13,
|
|
||||||
14,
|
|
||||||
15,
|
|
||||||
6,
|
|
||||||
7,
|
|
||||||
9,
|
|
||||||
8,
|
|
||||||
7,
|
|
||||||
6,
|
|
||||||
8,
|
|
||||||
13,
|
|
||||||
11,
|
|
||||||
9,
|
|
||||||
7,
|
|
||||||
15,
|
|
||||||
7,
|
|
||||||
12,
|
|
||||||
15,
|
|
||||||
9,
|
|
||||||
11,
|
|
||||||
7,
|
|
||||||
13,
|
|
||||||
12,
|
|
||||||
11,
|
|
||||||
13,
|
|
||||||
6,
|
|
||||||
7,
|
|
||||||
14,
|
|
||||||
9,
|
|
||||||
13,
|
|
||||||
15,
|
|
||||||
14,
|
|
||||||
8,
|
|
||||||
13,
|
|
||||||
6,
|
|
||||||
5,
|
|
||||||
12,
|
|
||||||
7,
|
|
||||||
5,
|
|
||||||
11,
|
|
||||||
12,
|
|
||||||
14,
|
|
||||||
15,
|
|
||||||
14,
|
|
||||||
15,
|
|
||||||
9,
|
|
||||||
8,
|
|
||||||
9,
|
|
||||||
14,
|
|
||||||
5,
|
|
||||||
6,
|
|
||||||
8,
|
|
||||||
6,
|
|
||||||
5,
|
|
||||||
12,
|
|
||||||
9,
|
|
||||||
15,
|
|
||||||
5,
|
|
||||||
11,
|
|
||||||
6,
|
|
||||||
8,
|
|
||||||
13,
|
|
||||||
12,
|
|
||||||
5,
|
|
||||||
12,
|
|
||||||
13,
|
|
||||||
14,
|
|
||||||
11,
|
|
||||||
8,
|
|
||||||
5,
|
|
||||||
6
|
|
||||||
]);
|
|
||||||
const _sr = WordArray.create([
|
|
||||||
8,
|
|
||||||
9,
|
|
||||||
9,
|
|
||||||
11,
|
|
||||||
13,
|
|
||||||
15,
|
|
||||||
15,
|
|
||||||
5,
|
|
||||||
7,
|
|
||||||
7,
|
|
||||||
8,
|
|
||||||
11,
|
|
||||||
14,
|
|
||||||
14,
|
|
||||||
12,
|
|
||||||
6,
|
|
||||||
9,
|
|
||||||
13,
|
|
||||||
15,
|
|
||||||
7,
|
|
||||||
12,
|
|
||||||
8,
|
|
||||||
9,
|
|
||||||
11,
|
|
||||||
7,
|
|
||||||
7,
|
|
||||||
12,
|
|
||||||
7,
|
|
||||||
6,
|
|
||||||
15,
|
|
||||||
13,
|
|
||||||
11,
|
|
||||||
9,
|
|
||||||
7,
|
|
||||||
15,
|
|
||||||
11,
|
|
||||||
8,
|
|
||||||
6,
|
|
||||||
6,
|
|
||||||
14,
|
|
||||||
12,
|
|
||||||
13,
|
|
||||||
5,
|
|
||||||
14,
|
|
||||||
13,
|
|
||||||
13,
|
|
||||||
7,
|
|
||||||
5,
|
|
||||||
15,
|
|
||||||
5,
|
|
||||||
8,
|
|
||||||
11,
|
|
||||||
14,
|
|
||||||
14,
|
|
||||||
6,
|
|
||||||
14,
|
|
||||||
6,
|
|
||||||
9,
|
|
||||||
12,
|
|
||||||
9,
|
|
||||||
12,
|
|
||||||
5,
|
|
||||||
15,
|
|
||||||
8,
|
|
||||||
8,
|
|
||||||
5,
|
|
||||||
12,
|
|
||||||
9,
|
|
||||||
12,
|
|
||||||
5,
|
|
||||||
14,
|
|
||||||
6,
|
|
||||||
8,
|
|
||||||
13,
|
|
||||||
6,
|
|
||||||
5,
|
|
||||||
15,
|
|
||||||
13,
|
|
||||||
11,
|
|
||||||
11
|
|
||||||
]);
|
|
||||||
const _hl = WordArray.create([
|
|
||||||
0,
|
|
||||||
1518500249,
|
|
||||||
1859775393,
|
|
||||||
2400959708,
|
|
||||||
2840853838
|
|
||||||
]);
|
|
||||||
const _hr = WordArray.create([
|
|
||||||
1352829926,
|
|
||||||
1548603684,
|
|
||||||
1836072691,
|
|
||||||
2053994217,
|
|
||||||
0
|
|
||||||
]);
|
|
||||||
/**
|
|
||||||
* RIPEMD160 round function 1
|
|
||||||
*/
|
|
||||||
const f1 = (x, y, z) => x ^ y ^ z;
|
|
||||||
/**
|
|
||||||
* RIPEMD160 round function 2
|
|
||||||
*/
|
|
||||||
const f2 = (x, y, z) => x & y | ~x & z;
|
|
||||||
/**
|
|
||||||
* RIPEMD160 round function 3
|
|
||||||
*/
|
|
||||||
const f3 = (x, y, z) => (x | ~y) ^ z;
|
|
||||||
/**
|
|
||||||
* RIPEMD160 round function 4
|
|
||||||
*/
|
|
||||||
const f4 = (x, y, z) => x & z | y & ~z;
|
|
||||||
/**
|
|
||||||
* RIPEMD160 round function 5
|
|
||||||
*/
|
|
||||||
const f5 = (x, y, z) => x ^ (y | ~z);
|
|
||||||
/**
|
|
||||||
* Rotate left helper
|
|
||||||
*/
|
|
||||||
const rotl = (x, n) => x << n | x >>> 32 - n;
|
|
||||||
/**
|
|
||||||
* RIPEMD160 hash algorithm.
|
|
||||||
*/
|
|
||||||
var RIPEMD160Algo = class extends Hasher32 {
|
|
||||||
_doReset() {
|
|
||||||
this._hash = WordArray.create([
|
|
||||||
1732584193,
|
|
||||||
4023233417,
|
|
||||||
2562383102,
|
|
||||||
271733878,
|
|
||||||
3285377520
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
_doProcessBlock(M, offset) {
|
|
||||||
const _M = M;
|
|
||||||
for (let i = 0; i < 16; i += 1) {
|
|
||||||
const offset_i = offset + i;
|
|
||||||
const M_offset_i = _M[offset_i];
|
|
||||||
_M[offset_i] = (M_offset_i << 8 | M_offset_i >>> 24) & 16711935 | (M_offset_i << 24 | M_offset_i >>> 8) & 4278255360;
|
|
||||||
}
|
|
||||||
const H = this._hash.words;
|
|
||||||
const hl = _hl.words;
|
|
||||||
const hr = _hr.words;
|
|
||||||
const zl = _zl.words;
|
|
||||||
const zr = _zr.words;
|
|
||||||
const sl = _sl.words;
|
|
||||||
const sr = _sr.words;
|
|
||||||
let al = H[0];
|
|
||||||
let bl = H[1];
|
|
||||||
let cl = H[2];
|
|
||||||
let dl = H[3];
|
|
||||||
let el = H[4];
|
|
||||||
let ar = H[0];
|
|
||||||
let br = H[1];
|
|
||||||
let cr = H[2];
|
|
||||||
let dr = H[3];
|
|
||||||
let er = H[4];
|
|
||||||
let t;
|
|
||||||
for (let i = 0; i < 80; i += 1) {
|
|
||||||
t = al + _M[offset + zl[i]] | 0;
|
|
||||||
if (i < 16) t += f1(bl, cl, dl) + hl[0];
|
|
||||||
else if (i < 32) t += f2(bl, cl, dl) + hl[1];
|
|
||||||
else if (i < 48) t += f3(bl, cl, dl) + hl[2];
|
|
||||||
else if (i < 64) t += f4(bl, cl, dl) + hl[3];
|
|
||||||
else t += f5(bl, cl, dl) + hl[4];
|
|
||||||
t |= 0;
|
|
||||||
t = rotl(t, sl[i]);
|
|
||||||
t = t + el | 0;
|
|
||||||
al = el;
|
|
||||||
el = dl;
|
|
||||||
dl = rotl(cl, 10);
|
|
||||||
cl = bl;
|
|
||||||
bl = t;
|
|
||||||
t = ar + _M[offset + zr[i]] | 0;
|
|
||||||
if (i < 16) t += f5(br, cr, dr) + hr[0];
|
|
||||||
else if (i < 32) t += f4(br, cr, dr) + hr[1];
|
|
||||||
else if (i < 48) t += f3(br, cr, dr) + hr[2];
|
|
||||||
else if (i < 64) t += f2(br, cr, dr) + hr[3];
|
|
||||||
else t += f1(br, cr, dr) + hr[4];
|
|
||||||
t |= 0;
|
|
||||||
t = rotl(t, sr[i]);
|
|
||||||
t = t + er | 0;
|
|
||||||
ar = er;
|
|
||||||
er = dr;
|
|
||||||
dr = rotl(cr, 10);
|
|
||||||
cr = br;
|
|
||||||
br = t;
|
|
||||||
}
|
|
||||||
t = H[1] + cl + dr | 0;
|
|
||||||
H[1] = H[2] + dl + er | 0;
|
|
||||||
H[2] = H[3] + el + ar | 0;
|
|
||||||
H[3] = H[4] + al + br | 0;
|
|
||||||
H[4] = H[0] + bl + cr | 0;
|
|
||||||
H[0] = t;
|
|
||||||
}
|
|
||||||
_doFinalize() {
|
|
||||||
const data = this._data;
|
|
||||||
const dataWords = data.words;
|
|
||||||
const nBitsTotal = this._nDataBytes * 8;
|
|
||||||
const nBitsLeft = data.sigBytes * 8;
|
|
||||||
dataWords[nBitsLeft >>> 5] |= 128 << 24 - nBitsLeft % 32;
|
|
||||||
dataWords[(nBitsLeft + 64 >>> 9 << 4) + 14] = (nBitsTotal << 8 | nBitsTotal >>> 24) & 16711935 | (nBitsTotal << 24 | nBitsTotal >>> 8) & 4278255360;
|
|
||||||
data.sigBytes = (dataWords.length + 1) * 4;
|
|
||||||
this._process();
|
|
||||||
const hash = this._hash;
|
|
||||||
const H = hash.words;
|
|
||||||
for (let i = 0; i < 5; i += 1) {
|
|
||||||
const H_i = H[i];
|
|
||||||
H[i] = (H_i << 8 | H_i >>> 24) & 16711935 | (H_i << 24 | H_i >>> 8) & 4278255360;
|
|
||||||
}
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
clone() {
|
|
||||||
const clone = super.clone.call(this);
|
|
||||||
clone._hash = this._hash.clone();
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Shortcut function to the hasher's object interface.
|
|
||||||
*
|
|
||||||
* @param message - The message to hash.
|
|
||||||
* @returns The hash.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```js
|
|
||||||
* const hash = RIPEMD160('message');
|
|
||||||
* const hash = RIPEMD160(wordArray);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
const RIPEMD160 = Hasher._createHelper(RIPEMD160Algo);
|
|
||||||
/**
|
|
||||||
* Shortcut function to the HMAC's object interface.
|
|
||||||
*
|
|
||||||
* @param message - The message to hash.
|
|
||||||
* @param key - The secret key.
|
|
||||||
* @returns The HMAC.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```js
|
|
||||||
* const hmac = HmacRIPEMD160(message, key);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
const HmacRIPEMD160 = Hasher._createHmacHelper(RIPEMD160Algo);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { HmacRIPEMD160, RIPEMD160, RIPEMD160Algo };
|
|
||||||
//# sourceMappingURL=ripemd160.mjs.map
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
import { Hasher, Hasher32, WordArray } from "./core.mjs";
|
|
||||||
|
|
||||||
//#region src/sha1.ts
|
|
||||||
const W = [];
|
|
||||||
/**
|
|
||||||
* SHA-1 hash algorithm.
|
|
||||||
*/
|
|
||||||
var SHA1Algo = class extends Hasher32 {
|
|
||||||
_doReset() {
|
|
||||||
this._hash = new WordArray([
|
|
||||||
1732584193,
|
|
||||||
4023233417,
|
|
||||||
2562383102,
|
|
||||||
271733878,
|
|
||||||
3285377520
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
_doProcessBlock(M, offset) {
|
|
||||||
const H = this._hash.words;
|
|
||||||
let a = H[0];
|
|
||||||
let b = H[1];
|
|
||||||
let c = H[2];
|
|
||||||
let d = H[3];
|
|
||||||
let e = H[4];
|
|
||||||
for (let i = 0; i < 80; i += 1) {
|
|
||||||
if (i < 16) W[i] = M[offset + i] | 0;
|
|
||||||
else {
|
|
||||||
const n = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16];
|
|
||||||
W[i] = n << 1 | n >>> 31;
|
|
||||||
}
|
|
||||||
let t = (a << 5 | a >>> 27) + e + W[i];
|
|
||||||
if (i < 20) t += (b & c | ~b & d) + 1518500249;
|
|
||||||
else if (i < 40) t += (b ^ c ^ d) + 1859775393;
|
|
||||||
else if (i < 60) t += (b & c | b & d | c & d) - 1894007588;
|
|
||||||
else t += (b ^ c ^ d) - 899497514;
|
|
||||||
e = d;
|
|
||||||
d = c;
|
|
||||||
c = b << 30 | b >>> 2;
|
|
||||||
b = a;
|
|
||||||
a = t;
|
|
||||||
}
|
|
||||||
H[0] = H[0] + a | 0;
|
|
||||||
H[1] = H[1] + b | 0;
|
|
||||||
H[2] = H[2] + c | 0;
|
|
||||||
H[3] = H[3] + d | 0;
|
|
||||||
H[4] = H[4] + e | 0;
|
|
||||||
}
|
|
||||||
_doFinalize() {
|
|
||||||
const data = this._data;
|
|
||||||
const dataWords = data.words;
|
|
||||||
const nBitsTotal = this._nDataBytes * 8;
|
|
||||||
const nBitsLeft = data.sigBytes * 8;
|
|
||||||
dataWords[nBitsLeft >>> 5] |= 128 << 24 - nBitsLeft % 32;
|
|
||||||
dataWords[(nBitsLeft + 64 >>> 9 << 4) + 14] = Math.floor(nBitsTotal / 4294967296);
|
|
||||||
dataWords[(nBitsLeft + 64 >>> 9 << 4) + 15] = nBitsTotal;
|
|
||||||
data.sigBytes = dataWords.length * 4;
|
|
||||||
this._process();
|
|
||||||
return this._hash;
|
|
||||||
}
|
|
||||||
clone() {
|
|
||||||
const clone = super.clone.call(this);
|
|
||||||
clone._hash = this._hash.clone();
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Shortcut function to the hasher's object interface.
|
|
||||||
*
|
|
||||||
* @param message - The message to hash.
|
|
||||||
* @returns The hash.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```js
|
|
||||||
* const hash = SHA1('message');
|
|
||||||
* const hash = SHA1(wordArray);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
const SHA1 = Hasher._createHelper(SHA1Algo);
|
|
||||||
/**
|
|
||||||
* Shortcut function to the HMAC's object interface.
|
|
||||||
*
|
|
||||||
* @param message - The message to hash.
|
|
||||||
* @param key - The secret key.
|
|
||||||
* @returns The HMAC.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```js
|
|
||||||
* const hmac = HmacSHA1(message, key);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
const HmacSHA1 = Hasher._createHmacHelper(SHA1Algo);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { HmacSHA1, SHA1, SHA1Algo };
|
|
||||||
//# sourceMappingURL=sha1.mjs.map
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
import { WordArray } from "./core.mjs";
|
|
||||||
import { SHA256Algo } from "./sha256.mjs";
|
|
||||||
|
|
||||||
//#region src/sha224.ts
|
|
||||||
/**
|
|
||||||
* SHA-224 hash algorithm.
|
|
||||||
*/
|
|
||||||
var SHA224Algo = class extends SHA256Algo {
|
|
||||||
_doReset() {
|
|
||||||
this._hash = new WordArray([
|
|
||||||
3238371032,
|
|
||||||
914150663,
|
|
||||||
812702999,
|
|
||||||
4144912697,
|
|
||||||
4290775857,
|
|
||||||
1750603025,
|
|
||||||
1694076839,
|
|
||||||
3204075428
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
_doFinalize() {
|
|
||||||
const hash = super._doFinalize.call(this);
|
|
||||||
hash.sigBytes -= 4;
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
clone() {
|
|
||||||
const clone = super.clone.call(this);
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Shortcut function to the hasher's object interface.
|
|
||||||
*
|
|
||||||
* @param message - The message to hash.
|
|
||||||
* @returns The hash.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```js
|
|
||||||
* const hash = SHA224('message');
|
|
||||||
* const hash = SHA224(wordArray);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
const SHA224 = SHA256Algo._createHelper(SHA224Algo);
|
|
||||||
/**
|
|
||||||
* Shortcut function to the HMAC's object interface.
|
|
||||||
*
|
|
||||||
* @param message - The message to hash.
|
|
||||||
* @param key - The secret key.
|
|
||||||
* @returns The HMAC.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```js
|
|
||||||
* const hmac = HmacSHA224(message, key);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
const HmacSHA224 = SHA256Algo._createHmacHelper(SHA224Algo);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { HmacSHA224, SHA224, SHA224Algo };
|
|
||||||
//# sourceMappingURL=sha224.mjs.map
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
import { Hasher, Hasher32, WordArray } from "./core.mjs";
|
|
||||||
|
|
||||||
//#region src/sha256.ts
|
|
||||||
const { H, K } = /* @__PURE__ */ (() => {
|
|
||||||
const _H = [];
|
|
||||||
const _K = [];
|
|
||||||
const isPrime = (n$1) => {
|
|
||||||
const sqrtN = Math.sqrt(n$1);
|
|
||||||
for (let factor = 2; factor <= sqrtN; factor += 1) if (!(n$1 % factor)) return false;
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
const getFractionalBits = (n$1) => (n$1 - (n$1 | 0)) * 4294967296 | 0;
|
|
||||||
let n = 2;
|
|
||||||
let nPrime = 0;
|
|
||||||
while (nPrime < 64) {
|
|
||||||
if (isPrime(n)) {
|
|
||||||
if (nPrime < 8) _H[nPrime] = getFractionalBits(n ** (1 / 2));
|
|
||||||
_K[nPrime] = getFractionalBits(n ** (1 / 3));
|
|
||||||
nPrime += 1;
|
|
||||||
}
|
|
||||||
n += 1;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
H: _H,
|
|
||||||
K: _K
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
const W = [];
|
|
||||||
/**
|
|
||||||
* SHA-256 hash algorithm.
|
|
||||||
*/
|
|
||||||
var SHA256Algo = class extends Hasher32 {
|
|
||||||
_doReset() {
|
|
||||||
this._hash = new WordArray(H.slice(0));
|
|
||||||
}
|
|
||||||
_doProcessBlock(M, offset) {
|
|
||||||
const _H = this._hash.words;
|
|
||||||
let a = _H[0];
|
|
||||||
let b = _H[1];
|
|
||||||
let c = _H[2];
|
|
||||||
let d = _H[3];
|
|
||||||
let e = _H[4];
|
|
||||||
let f = _H[5];
|
|
||||||
let g = _H[6];
|
|
||||||
let h = _H[7];
|
|
||||||
for (let i = 0; i < 64; i += 1) {
|
|
||||||
if (i < 16) W[i] = M[offset + i] | 0;
|
|
||||||
else {
|
|
||||||
const gamma0x = W[i - 15];
|
|
||||||
const gamma0 = (gamma0x << 25 | gamma0x >>> 7) ^ (gamma0x << 14 | gamma0x >>> 18) ^ gamma0x >>> 3;
|
|
||||||
const gamma1x = W[i - 2];
|
|
||||||
const gamma1 = (gamma1x << 15 | gamma1x >>> 17) ^ (gamma1x << 13 | gamma1x >>> 19) ^ gamma1x >>> 10;
|
|
||||||
W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16];
|
|
||||||
}
|
|
||||||
const ch = e & f ^ ~e & g;
|
|
||||||
const maj = a & b ^ a & c ^ b & c;
|
|
||||||
const sigma0 = (a << 30 | a >>> 2) ^ (a << 19 | a >>> 13) ^ (a << 10 | a >>> 22);
|
|
||||||
const sigma1 = (e << 26 | e >>> 6) ^ (e << 21 | e >>> 11) ^ (e << 7 | e >>> 25);
|
|
||||||
const t1 = h + sigma1 + ch + K[i] + W[i];
|
|
||||||
const t2 = sigma0 + maj;
|
|
||||||
h = g;
|
|
||||||
g = f;
|
|
||||||
f = e;
|
|
||||||
e = d + t1 | 0;
|
|
||||||
d = c;
|
|
||||||
c = b;
|
|
||||||
b = a;
|
|
||||||
a = t1 + t2 | 0;
|
|
||||||
}
|
|
||||||
_H[0] = _H[0] + a | 0;
|
|
||||||
_H[1] = _H[1] + b | 0;
|
|
||||||
_H[2] = _H[2] + c | 0;
|
|
||||||
_H[3] = _H[3] + d | 0;
|
|
||||||
_H[4] = _H[4] + e | 0;
|
|
||||||
_H[5] = _H[5] + f | 0;
|
|
||||||
_H[6] = _H[6] + g | 0;
|
|
||||||
_H[7] = _H[7] + h | 0;
|
|
||||||
}
|
|
||||||
_doFinalize() {
|
|
||||||
const data = this._data;
|
|
||||||
const dataWords = data.words;
|
|
||||||
const nBitsTotal = this._nDataBytes * 8;
|
|
||||||
const nBitsLeft = data.sigBytes * 8;
|
|
||||||
dataWords[nBitsLeft >>> 5] |= 128 << 24 - nBitsLeft % 32;
|
|
||||||
dataWords[(nBitsLeft + 64 >>> 9 << 4) + 14] = Math.floor(nBitsTotal / 4294967296);
|
|
||||||
dataWords[(nBitsLeft + 64 >>> 9 << 4) + 15] = nBitsTotal;
|
|
||||||
data.sigBytes = dataWords.length * 4;
|
|
||||||
this._process();
|
|
||||||
return this._hash;
|
|
||||||
}
|
|
||||||
clone() {
|
|
||||||
const clone = super.clone.call(this);
|
|
||||||
clone._hash = this._hash.clone();
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Shortcut function to the hasher's object interface.
|
|
||||||
*
|
|
||||||
* @param message - The message to hash.
|
|
||||||
* @returns The hash.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```js
|
|
||||||
* const hash = SHA256('message');
|
|
||||||
* const hash = SHA256(wordArray);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
const SHA256 = Hasher._createHelper(SHA256Algo);
|
|
||||||
/**
|
|
||||||
* Shortcut function to the HMAC's object interface.
|
|
||||||
*
|
|
||||||
* @param message - The message to hash.
|
|
||||||
* @param key - The secret key.
|
|
||||||
* @returns The HMAC.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```js
|
|
||||||
* const hmac = HmacSHA256(message, key);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
const HmacSHA256 = Hasher._createHmacHelper(SHA256Algo);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { HmacSHA256, SHA256, SHA256Algo };
|
|
||||||
//# sourceMappingURL=sha256.mjs.map
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
import { Hasher, Hasher32, WordArray } from "./core.mjs";
|
|
||||||
import { X64Word } from "./x64-core.mjs";
|
|
||||||
|
|
||||||
//#region src/sha3.ts
|
|
||||||
const RHO_OFFSETS = [];
|
|
||||||
const PI_INDEXES = [];
|
|
||||||
const ROUND_CONSTANTS = [];
|
|
||||||
const T = /* @__PURE__ */ (() => {
|
|
||||||
const a = [];
|
|
||||||
for (let i = 0; i < 25; i += 1) a[i] = X64Word.create();
|
|
||||||
return a;
|
|
||||||
})();
|
|
||||||
/**
|
|
||||||
* SHA-3 hash algorithm.
|
|
||||||
*/
|
|
||||||
var SHA3Algo = class extends Hasher32 {
|
|
||||||
_state = [];
|
|
||||||
/**
|
|
||||||
* Initializes a newly created hasher.
|
|
||||||
*
|
|
||||||
* @param cfg - Configuration options.
|
|
||||||
* @property {number} outputLength - The desired number of bits in the output hash.
|
|
||||||
* Only values permitted are: 224, 256, 384, 512.
|
|
||||||
* Default: 512
|
|
||||||
*/
|
|
||||||
constructor(cfg) {
|
|
||||||
super(Object.assign({ outputLength: 512 }, cfg));
|
|
||||||
}
|
|
||||||
_doReset() {
|
|
||||||
this._state = [];
|
|
||||||
for (let i = 0; i < 25; i += 1) this._state[i] = new X64Word();
|
|
||||||
this.blockSize = (1600 - 2 * this.cfg.outputLength) / 32;
|
|
||||||
}
|
|
||||||
_doProcessBlock(M, offset) {
|
|
||||||
if (this._state.length === 0) this._doReset();
|
|
||||||
const state = this._state;
|
|
||||||
const nBlockSizeLanes = this.blockSize / 2;
|
|
||||||
for (let i = 0; i < nBlockSizeLanes; i += 1) {
|
|
||||||
let M2i = M[offset + 2 * i];
|
|
||||||
let M2i1 = M[offset + 2 * i + 1];
|
|
||||||
M2i = (M2i << 8 | M2i >>> 24) & 16711935 | (M2i << 24 | M2i >>> 8) & 4278255360;
|
|
||||||
M2i1 = (M2i1 << 8 | M2i1 >>> 24) & 16711935 | (M2i1 << 24 | M2i1 >>> 8) & 4278255360;
|
|
||||||
const lane = state[i];
|
|
||||||
lane.high ^= M2i1;
|
|
||||||
lane.low ^= M2i;
|
|
||||||
}
|
|
||||||
for (let round = 0; round < 24; round += 1) {
|
|
||||||
for (let x = 0; x < 5; x += 1) {
|
|
||||||
let tMsw = 0;
|
|
||||||
let tLsw = 0;
|
|
||||||
for (let y = 0; y < 5; y += 1) {
|
|
||||||
const lane$1 = state[x + 5 * y];
|
|
||||||
tMsw ^= lane$1.high;
|
|
||||||
tLsw ^= lane$1.low;
|
|
||||||
}
|
|
||||||
const Tx = T[x];
|
|
||||||
Tx.high = tMsw;
|
|
||||||
Tx.low = tLsw;
|
|
||||||
}
|
|
||||||
for (let x = 0; x < 5; x += 1) {
|
|
||||||
const Tx4 = T[(x + 4) % 5];
|
|
||||||
const Tx1 = T[(x + 1) % 5];
|
|
||||||
const Tx1Msw = Tx1.high;
|
|
||||||
const Tx1Lsw = Tx1.low;
|
|
||||||
const tMsw = Tx4.high ^ (Tx1Msw << 1 | Tx1Lsw >>> 31);
|
|
||||||
const tLsw = Tx4.low ^ (Tx1Lsw << 1 | Tx1Msw >>> 31);
|
|
||||||
for (let y = 0; y < 5; y += 1) {
|
|
||||||
const lane$1 = state[x + 5 * y];
|
|
||||||
lane$1.high ^= tMsw;
|
|
||||||
lane$1.low ^= tLsw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let laneIndex = 1; laneIndex < 25; laneIndex += 1) {
|
|
||||||
let tMsw;
|
|
||||||
let tLsw;
|
|
||||||
const lane$1 = state[laneIndex];
|
|
||||||
const laneMsw = lane$1.high;
|
|
||||||
const laneLsw = lane$1.low;
|
|
||||||
const rhoOffset = RHO_OFFSETS[laneIndex];
|
|
||||||
if (rhoOffset < 32) {
|
|
||||||
tMsw = laneMsw << rhoOffset | laneLsw >>> 32 - rhoOffset;
|
|
||||||
tLsw = laneLsw << rhoOffset | laneMsw >>> 32 - rhoOffset;
|
|
||||||
} else {
|
|
||||||
tMsw = laneLsw << rhoOffset - 32 | laneMsw >>> 64 - rhoOffset;
|
|
||||||
tLsw = laneMsw << rhoOffset - 32 | laneLsw >>> 64 - rhoOffset;
|
|
||||||
}
|
|
||||||
const TPiLane = T[PI_INDEXES[laneIndex]];
|
|
||||||
TPiLane.high = tMsw;
|
|
||||||
TPiLane.low = tLsw;
|
|
||||||
}
|
|
||||||
const T0 = T[0];
|
|
||||||
const state0 = state[0];
|
|
||||||
T0.high = state0.high;
|
|
||||||
T0.low = state0.low;
|
|
||||||
for (let x = 0; x < 5; x += 1) for (let y = 0; y < 5; y += 1) {
|
|
||||||
const laneIndex = x + 5 * y;
|
|
||||||
const lane$1 = state[laneIndex];
|
|
||||||
const TLane = T[laneIndex];
|
|
||||||
const Tx1Lane = T[(x + 1) % 5 + 5 * y];
|
|
||||||
const Tx2Lane = T[(x + 2) % 5 + 5 * y];
|
|
||||||
lane$1.high = TLane.high ^ ~Tx1Lane.high & Tx2Lane.high;
|
|
||||||
lane$1.low = TLane.low ^ ~Tx1Lane.low & Tx2Lane.low;
|
|
||||||
}
|
|
||||||
const lane = state[0];
|
|
||||||
const roundConstant = ROUND_CONSTANTS[round];
|
|
||||||
lane.high ^= roundConstant.high;
|
|
||||||
lane.low ^= roundConstant.low;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_doFinalize() {
|
|
||||||
const data = this._data;
|
|
||||||
const dataWords = data.words;
|
|
||||||
const nBitsLeft = data.sigBytes * 8;
|
|
||||||
const blockSizeBits = this.blockSize * 32;
|
|
||||||
dataWords[nBitsLeft >>> 5] |= 1 << 24 - nBitsLeft % 32;
|
|
||||||
dataWords[(Math.ceil((nBitsLeft + 1) / blockSizeBits) * blockSizeBits >>> 5) - 1] |= 128;
|
|
||||||
data.sigBytes = dataWords.length * 4;
|
|
||||||
this._process();
|
|
||||||
const state = this._state;
|
|
||||||
const outputLengthBytes = this.cfg.outputLength / 8;
|
|
||||||
const outputLengthLanes = outputLengthBytes / 8;
|
|
||||||
const hashWords = [];
|
|
||||||
for (let i = 0; i < outputLengthLanes; i += 1) {
|
|
||||||
const lane = state[i];
|
|
||||||
let laneMsw = lane.high;
|
|
||||||
let laneLsw = lane.low;
|
|
||||||
laneMsw = (laneMsw << 8 | laneMsw >>> 24) & 16711935 | (laneMsw << 24 | laneMsw >>> 8) & 4278255360;
|
|
||||||
laneLsw = (laneLsw << 8 | laneLsw >>> 24) & 16711935 | (laneLsw << 24 | laneLsw >>> 8) & 4278255360;
|
|
||||||
hashWords.push(laneLsw);
|
|
||||||
hashWords.push(laneMsw);
|
|
||||||
}
|
|
||||||
return new WordArray(hashWords, outputLengthBytes);
|
|
||||||
}
|
|
||||||
clone() {
|
|
||||||
const clone = super.clone.call(this);
|
|
||||||
clone._state = [];
|
|
||||||
for (let i = 0; i < this._state.length; i += 1) clone._state[i] = this._state[i].clone();
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Shortcut function to the hasher's object interface.
|
|
||||||
*
|
|
||||||
* @param message - The message to hash.
|
|
||||||
* @returns The hash.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```js
|
|
||||||
* const hash = SHA3('message');
|
|
||||||
* const hash = SHA3(wordArray);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
const SHA3 = Hasher._createHelper(SHA3Algo);
|
|
||||||
/**
|
|
||||||
* Shortcut function to the HMAC's object interface.
|
|
||||||
*
|
|
||||||
* @param message - The message to hash.
|
|
||||||
* @param key - The secret key.
|
|
||||||
* @returns The HMAC.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```js
|
|
||||||
* const hmac = HmacSHA3(message, key);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
const HmacSHA3 = Hasher._createHmacHelper(SHA3Algo);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { HmacSHA3, SHA3, SHA3Algo };
|
|
||||||
//# sourceMappingURL=sha3.mjs.map
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
import { X64Word, X64WordArray } from "./x64-core.mjs";
|
|
||||||
import { SHA512Algo } from "./sha512.mjs";
|
|
||||||
|
|
||||||
//#region src/sha384.ts
|
|
||||||
/**
|
|
||||||
* SHA-384 hash algorithm.
|
|
||||||
*/
|
|
||||||
var SHA384Algo = class extends SHA512Algo {
|
|
||||||
_doReset() {
|
|
||||||
this._hash = new X64WordArray([
|
|
||||||
new X64Word(3418070365, 3238371032),
|
|
||||||
new X64Word(1654270250, 914150663),
|
|
||||||
new X64Word(2438529370, 812702999),
|
|
||||||
new X64Word(355462360, 4144912697),
|
|
||||||
new X64Word(1731405415, 4290775857),
|
|
||||||
new X64Word(2394180231, 1750603025),
|
|
||||||
new X64Word(3675008525, 1694076839),
|
|
||||||
new X64Word(1203062813, 3204075428)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
_doFinalize() {
|
|
||||||
const hash = super._doFinalize.call(this);
|
|
||||||
hash.sigBytes -= 16;
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
clone() {
|
|
||||||
const clone = super.clone.call(this);
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Shortcut function to the hasher's object interface.
|
|
||||||
*
|
|
||||||
* @param message - The message to hash.
|
|
||||||
* @returns The hash.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```js
|
|
||||||
* const hash = SHA384('message');
|
|
||||||
* const hash = SHA384(wordArray);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
const SHA384 = SHA512Algo._createHelper(SHA384Algo);
|
|
||||||
/**
|
|
||||||
* Shortcut function to the HMAC's object interface.
|
|
||||||
*
|
|
||||||
* @param message - The message to hash.
|
|
||||||
* @param key - The secret key.
|
|
||||||
* @returns The HMAC.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```js
|
|
||||||
* const hmac = HmacSHA384(message, key);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
const HmacSHA384 = SHA512Algo._createHmacHelper(SHA384Algo);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { HmacSHA384, SHA384, SHA384Algo };
|
|
||||||
//# sourceMappingURL=sha384.mjs.map
|
|
||||||
@@ -1,300 +0,0 @@
|
|||||||
import { Hasher, Hasher64 } from "./core.mjs";
|
|
||||||
import { X64Word, X64WordArray } from "./x64-core.mjs";
|
|
||||||
|
|
||||||
//#region src/sha512.ts
|
|
||||||
const K = [
|
|
||||||
new X64Word(1116352408, 3609767458),
|
|
||||||
new X64Word(1899447441, 602891725),
|
|
||||||
new X64Word(3049323471, 3964484399),
|
|
||||||
new X64Word(3921009573, 2173295548),
|
|
||||||
new X64Word(961987163, 4081628472),
|
|
||||||
new X64Word(1508970993, 3053834265),
|
|
||||||
new X64Word(2453635748, 2937671579),
|
|
||||||
new X64Word(2870763221, 3664609560),
|
|
||||||
new X64Word(3624381080, 2734883394),
|
|
||||||
new X64Word(310598401, 1164996542),
|
|
||||||
new X64Word(607225278, 1323610764),
|
|
||||||
new X64Word(1426881987, 3590304994),
|
|
||||||
new X64Word(1925078388, 4068182383),
|
|
||||||
new X64Word(2162078206, 991336113),
|
|
||||||
new X64Word(2614888103, 633803317),
|
|
||||||
new X64Word(3248222580, 3479774868),
|
|
||||||
new X64Word(3835390401, 2666613458),
|
|
||||||
new X64Word(4022224774, 944711139),
|
|
||||||
new X64Word(264347078, 2341262773),
|
|
||||||
new X64Word(604807628, 2007800933),
|
|
||||||
new X64Word(770255983, 1495990901),
|
|
||||||
new X64Word(1249150122, 1856431235),
|
|
||||||
new X64Word(1555081692, 3175218132),
|
|
||||||
new X64Word(1996064986, 2198950837),
|
|
||||||
new X64Word(2554220882, 3999719339),
|
|
||||||
new X64Word(2821834349, 766784016),
|
|
||||||
new X64Word(2952996808, 2566594879),
|
|
||||||
new X64Word(3210313671, 3203337956),
|
|
||||||
new X64Word(3336571891, 1034457026),
|
|
||||||
new X64Word(3584528711, 2466948901),
|
|
||||||
new X64Word(113926993, 3758326383),
|
|
||||||
new X64Word(338241895, 168717936),
|
|
||||||
new X64Word(666307205, 1188179964),
|
|
||||||
new X64Word(773529912, 1546045734),
|
|
||||||
new X64Word(1294757372, 1522805485),
|
|
||||||
new X64Word(1396182291, 2643833823),
|
|
||||||
new X64Word(1695183700, 2343527390),
|
|
||||||
new X64Word(1986661051, 1014477480),
|
|
||||||
new X64Word(2177026350, 1206759142),
|
|
||||||
new X64Word(2456956037, 344077627),
|
|
||||||
new X64Word(2730485921, 1290863460),
|
|
||||||
new X64Word(2820302411, 3158454273),
|
|
||||||
new X64Word(3259730800, 3505952657),
|
|
||||||
new X64Word(3345764771, 106217008),
|
|
||||||
new X64Word(3516065817, 3606008344),
|
|
||||||
new X64Word(3600352804, 1432725776),
|
|
||||||
new X64Word(4094571909, 1467031594),
|
|
||||||
new X64Word(275423344, 851169720),
|
|
||||||
new X64Word(430227734, 3100823752),
|
|
||||||
new X64Word(506948616, 1363258195),
|
|
||||||
new X64Word(659060556, 3750685593),
|
|
||||||
new X64Word(883997877, 3785050280),
|
|
||||||
new X64Word(958139571, 3318307427),
|
|
||||||
new X64Word(1322822218, 3812723403),
|
|
||||||
new X64Word(1537002063, 2003034995),
|
|
||||||
new X64Word(1747873779, 3602036899),
|
|
||||||
new X64Word(1955562222, 1575990012),
|
|
||||||
new X64Word(2024104815, 1125592928),
|
|
||||||
new X64Word(2227730452, 2716904306),
|
|
||||||
new X64Word(2361852424, 442776044),
|
|
||||||
new X64Word(2428436474, 593698344),
|
|
||||||
new X64Word(2756734187, 3733110249),
|
|
||||||
new X64Word(3204031479, 2999351573),
|
|
||||||
new X64Word(3329325298, 3815920427),
|
|
||||||
new X64Word(3391569614, 3928383900),
|
|
||||||
new X64Word(3515267271, 566280711),
|
|
||||||
new X64Word(3940187606, 3454069534),
|
|
||||||
new X64Word(4118630271, 4000239992),
|
|
||||||
new X64Word(116418474, 1914138554),
|
|
||||||
new X64Word(174292421, 2731055270),
|
|
||||||
new X64Word(289380356, 3203993006),
|
|
||||||
new X64Word(460393269, 320620315),
|
|
||||||
new X64Word(685471733, 587496836),
|
|
||||||
new X64Word(852142971, 1086792851),
|
|
||||||
new X64Word(1017036298, 365543100),
|
|
||||||
new X64Word(1126000580, 2618297676),
|
|
||||||
new X64Word(1288033470, 3409855158),
|
|
||||||
new X64Word(1501505948, 4234509866),
|
|
||||||
new X64Word(1607167915, 987167468),
|
|
||||||
new X64Word(1816402316, 1246189591)
|
|
||||||
];
|
|
||||||
const W = /* @__PURE__ */ (() => {
|
|
||||||
const a = [];
|
|
||||||
for (let i = 0; i < 80; i += 1) a[i] = new X64Word();
|
|
||||||
return a;
|
|
||||||
})();
|
|
||||||
/**
|
|
||||||
* SHA-512 hash algorithm.
|
|
||||||
*/
|
|
||||||
var SHA512Algo = class extends Hasher64 {
|
|
||||||
constructor(cfg) {
|
|
||||||
super(cfg);
|
|
||||||
this.blockSize = 1024 / 32;
|
|
||||||
}
|
|
||||||
_doReset() {
|
|
||||||
this._hash = new X64WordArray([
|
|
||||||
new X64Word(1779033703, 4089235720),
|
|
||||||
new X64Word(3144134277, 2227873595),
|
|
||||||
new X64Word(1013904242, 4271175723),
|
|
||||||
new X64Word(2773480762, 1595750129),
|
|
||||||
new X64Word(1359893119, 2917565137),
|
|
||||||
new X64Word(2600822924, 725511199),
|
|
||||||
new X64Word(528734635, 4215389547),
|
|
||||||
new X64Word(1541459225, 327033209)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
_doProcessBlock(M, offset) {
|
|
||||||
const H = this._hash.words;
|
|
||||||
const H0 = H[0];
|
|
||||||
const H1 = H[1];
|
|
||||||
const H2 = H[2];
|
|
||||||
const H3 = H[3];
|
|
||||||
const H4 = H[4];
|
|
||||||
const H5 = H[5];
|
|
||||||
const H6 = H[6];
|
|
||||||
const H7 = H[7];
|
|
||||||
const H0h = H0.high;
|
|
||||||
let H0l = H0.low;
|
|
||||||
const H1h = H1.high;
|
|
||||||
let H1l = H1.low;
|
|
||||||
const H2h = H2.high;
|
|
||||||
let H2l = H2.low;
|
|
||||||
const H3h = H3.high;
|
|
||||||
let H3l = H3.low;
|
|
||||||
const H4h = H4.high;
|
|
||||||
let H4l = H4.low;
|
|
||||||
const H5h = H5.high;
|
|
||||||
let H5l = H5.low;
|
|
||||||
const H6h = H6.high;
|
|
||||||
let H6l = H6.low;
|
|
||||||
const H7h = H7.high;
|
|
||||||
let H7l = H7.low;
|
|
||||||
let ah = H0h;
|
|
||||||
let al = H0l;
|
|
||||||
let bh = H1h;
|
|
||||||
let bl = H1l;
|
|
||||||
let ch = H2h;
|
|
||||||
let cl = H2l;
|
|
||||||
let dh = H3h;
|
|
||||||
let dl = H3l;
|
|
||||||
let eh = H4h;
|
|
||||||
let el = H4l;
|
|
||||||
let fh = H5h;
|
|
||||||
let fl = H5l;
|
|
||||||
let gh = H6h;
|
|
||||||
let gl = H6l;
|
|
||||||
let hh = H7h;
|
|
||||||
let hl = H7l;
|
|
||||||
for (let i = 0; i < 80; i += 1) {
|
|
||||||
let Wil;
|
|
||||||
let Wih;
|
|
||||||
const Wi = W[i];
|
|
||||||
if (i < 16) {
|
|
||||||
Wi.high = M[offset + i * 2] | 0;
|
|
||||||
Wih = Wi.high;
|
|
||||||
Wi.low = M[offset + i * 2 + 1] | 0;
|
|
||||||
Wil = Wi.low;
|
|
||||||
} else {
|
|
||||||
const gamma0x = W[i - 15];
|
|
||||||
const gamma0xh = gamma0x.high;
|
|
||||||
const gamma0xl = gamma0x.low;
|
|
||||||
const gamma0h = (gamma0xh >>> 1 | gamma0xl << 31) ^ (gamma0xh >>> 8 | gamma0xl << 24) ^ gamma0xh >>> 7;
|
|
||||||
const gamma0l = (gamma0xl >>> 1 | gamma0xh << 31) ^ (gamma0xl >>> 8 | gamma0xh << 24) ^ (gamma0xl >>> 7 | gamma0xh << 25);
|
|
||||||
const gamma1x = W[i - 2];
|
|
||||||
const gamma1xh = gamma1x.high;
|
|
||||||
const gamma1xl = gamma1x.low;
|
|
||||||
const gamma1h = (gamma1xh >>> 19 | gamma1xl << 13) ^ (gamma1xh << 3 | gamma1xl >>> 29) ^ gamma1xh >>> 6;
|
|
||||||
const gamma1l = (gamma1xl >>> 19 | gamma1xh << 13) ^ (gamma1xl << 3 | gamma1xh >>> 29) ^ (gamma1xl >>> 6 | gamma1xh << 26);
|
|
||||||
const Wi7 = W[i - 7];
|
|
||||||
const Wi7h = Wi7.high;
|
|
||||||
const Wi7l = Wi7.low;
|
|
||||||
const Wi16 = W[i - 16];
|
|
||||||
const Wi16h = Wi16.high;
|
|
||||||
const Wi16l = Wi16.low;
|
|
||||||
Wil = gamma0l + Wi7l;
|
|
||||||
Wih = gamma0h + Wi7h + (Wil >>> 0 < gamma0l >>> 0 ? 1 : 0);
|
|
||||||
Wil += gamma1l;
|
|
||||||
Wih = Wih + gamma1h + (Wil >>> 0 < gamma1l >>> 0 ? 1 : 0);
|
|
||||||
Wil += Wi16l;
|
|
||||||
Wih = Wih + Wi16h + (Wil >>> 0 < Wi16l >>> 0 ? 1 : 0);
|
|
||||||
Wi.high = Wih;
|
|
||||||
Wi.low = Wil;
|
|
||||||
}
|
|
||||||
const chh = eh & fh ^ ~eh & gh;
|
|
||||||
const chl = el & fl ^ ~el & gl;
|
|
||||||
const majh = ah & bh ^ ah & ch ^ bh & ch;
|
|
||||||
const majl = al & bl ^ al & cl ^ bl & cl;
|
|
||||||
const sigma0h = (ah >>> 28 | al << 4) ^ (ah << 30 | al >>> 2) ^ (ah << 25 | al >>> 7);
|
|
||||||
const sigma0l = (al >>> 28 | ah << 4) ^ (al << 30 | ah >>> 2) ^ (al << 25 | ah >>> 7);
|
|
||||||
const sigma1h = (eh >>> 14 | el << 18) ^ (eh >>> 18 | el << 14) ^ (eh << 23 | el >>> 9);
|
|
||||||
const sigma1l = (el >>> 14 | eh << 18) ^ (el >>> 18 | eh << 14) ^ (el << 23 | eh >>> 9);
|
|
||||||
const Ki = K[i];
|
|
||||||
const Kih = Ki.high;
|
|
||||||
const Kil = Ki.low;
|
|
||||||
let t1l = hl + sigma1l;
|
|
||||||
let t1h = hh + sigma1h + (t1l >>> 0 < hl >>> 0 ? 1 : 0);
|
|
||||||
t1l += chl;
|
|
||||||
t1h = t1h + chh + (t1l >>> 0 < chl >>> 0 ? 1 : 0);
|
|
||||||
t1l += Kil;
|
|
||||||
t1h = t1h + Kih + (t1l >>> 0 < Kil >>> 0 ? 1 : 0);
|
|
||||||
t1l += Wil;
|
|
||||||
t1h = t1h + Wih + (t1l >>> 0 < Wil >>> 0 ? 1 : 0);
|
|
||||||
const t2l = sigma0l + majl;
|
|
||||||
const t2h = sigma0h + majh + (t2l >>> 0 < sigma0l >>> 0 ? 1 : 0);
|
|
||||||
hh = gh;
|
|
||||||
hl = gl;
|
|
||||||
gh = fh;
|
|
||||||
gl = fl;
|
|
||||||
fh = eh;
|
|
||||||
fl = el;
|
|
||||||
el = dl + t1l | 0;
|
|
||||||
eh = dh + t1h + (el >>> 0 < dl >>> 0 ? 1 : 0) | 0;
|
|
||||||
dh = ch;
|
|
||||||
dl = cl;
|
|
||||||
ch = bh;
|
|
||||||
cl = bl;
|
|
||||||
bh = ah;
|
|
||||||
bl = al;
|
|
||||||
al = t1l + t2l | 0;
|
|
||||||
ah = t1h + t2h + (al >>> 0 < t1l >>> 0 ? 1 : 0) | 0;
|
|
||||||
}
|
|
||||||
H0.low = H0l + al;
|
|
||||||
H0l = H0.low;
|
|
||||||
H0.high = H0h + ah + (H0l >>> 0 < al >>> 0 ? 1 : 0);
|
|
||||||
H1.low = H1l + bl;
|
|
||||||
H1l = H1.low;
|
|
||||||
H1.high = H1h + bh + (H1l >>> 0 < bl >>> 0 ? 1 : 0);
|
|
||||||
H2.low = H2l + cl;
|
|
||||||
H2l = H2.low;
|
|
||||||
H2.high = H2h + ch + (H2l >>> 0 < cl >>> 0 ? 1 : 0);
|
|
||||||
H3.low = H3l + dl;
|
|
||||||
H3l = H3.low;
|
|
||||||
H3.high = H3h + dh + (H3l >>> 0 < dl >>> 0 ? 1 : 0);
|
|
||||||
H4.low = H4l + el;
|
|
||||||
H4l = H4.low;
|
|
||||||
H4.high = H4h + eh + (H4l >>> 0 < el >>> 0 ? 1 : 0);
|
|
||||||
H5.low = H5l + fl;
|
|
||||||
H5l = H5.low;
|
|
||||||
H5.high = H5h + fh + (H5l >>> 0 < fl >>> 0 ? 1 : 0);
|
|
||||||
H6.low = H6l + gl;
|
|
||||||
H6l = H6.low;
|
|
||||||
H6.high = H6h + gh + (H6l >>> 0 < gl >>> 0 ? 1 : 0);
|
|
||||||
H7.low = H7l + hl;
|
|
||||||
H7l = H7.low;
|
|
||||||
H7.high = H7h + hh + (H7l >>> 0 < hl >>> 0 ? 1 : 0);
|
|
||||||
}
|
|
||||||
_doFinalize() {
|
|
||||||
const data = this._data;
|
|
||||||
const dataWords = data.words;
|
|
||||||
const nBitsTotal = this._nDataBytes * 8;
|
|
||||||
const nBitsLeft = data.sigBytes * 8;
|
|
||||||
dataWords[nBitsLeft >>> 5] |= 128 << 24 - nBitsLeft % 32;
|
|
||||||
dataWords[(nBitsLeft + 128 >>> 10 << 5) + 30] = Math.floor(nBitsTotal / 4294967296);
|
|
||||||
dataWords[(nBitsLeft + 128 >>> 10 << 5) + 31] = nBitsTotal;
|
|
||||||
data.sigBytes = dataWords.length * 4;
|
|
||||||
this._process();
|
|
||||||
const hash = this._hash.toX32();
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
clone() {
|
|
||||||
const clone = super.clone.call(this);
|
|
||||||
clone._hash = this._hash.clone();
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Shortcut function to the hasher's object interface.
|
|
||||||
*
|
|
||||||
* @param message - The message to hash.
|
|
||||||
* @returns The hash.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```js
|
|
||||||
* const hash = SHA512('message');
|
|
||||||
* const hash = SHA512(wordArray);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
const SHA512 = Hasher._createHelper(SHA512Algo);
|
|
||||||
/**
|
|
||||||
* Shortcut function to the HMAC's object interface.
|
|
||||||
*
|
|
||||||
* @param message - The message to hash.
|
|
||||||
* @param key - The secret key.
|
|
||||||
* @returns The HMAC.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```js
|
|
||||||
* const hmac = HmacSHA512(message, key);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
const HmacSHA512 = Hasher._createHmacHelper(SHA512Algo);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { HmacSHA512, SHA512, SHA512Algo };
|
|
||||||
//# sourceMappingURL=sha512.mjs.map
|
|
||||||
@@ -1,822 +0,0 @@
|
|||||||
import { WordArray } from "./core.mjs";
|
|
||||||
import { BlockCipher } from "./cipher-core.mjs";
|
|
||||||
|
|
||||||
//#region src/tripledes.ts
|
|
||||||
const PC1 = [
|
|
||||||
57,
|
|
||||||
49,
|
|
||||||
41,
|
|
||||||
33,
|
|
||||||
25,
|
|
||||||
17,
|
|
||||||
9,
|
|
||||||
1,
|
|
||||||
58,
|
|
||||||
50,
|
|
||||||
42,
|
|
||||||
34,
|
|
||||||
26,
|
|
||||||
18,
|
|
||||||
10,
|
|
||||||
2,
|
|
||||||
59,
|
|
||||||
51,
|
|
||||||
43,
|
|
||||||
35,
|
|
||||||
27,
|
|
||||||
19,
|
|
||||||
11,
|
|
||||||
3,
|
|
||||||
60,
|
|
||||||
52,
|
|
||||||
44,
|
|
||||||
36,
|
|
||||||
63,
|
|
||||||
55,
|
|
||||||
47,
|
|
||||||
39,
|
|
||||||
31,
|
|
||||||
23,
|
|
||||||
15,
|
|
||||||
7,
|
|
||||||
62,
|
|
||||||
54,
|
|
||||||
46,
|
|
||||||
38,
|
|
||||||
30,
|
|
||||||
22,
|
|
||||||
14,
|
|
||||||
6,
|
|
||||||
61,
|
|
||||||
53,
|
|
||||||
45,
|
|
||||||
37,
|
|
||||||
29,
|
|
||||||
21,
|
|
||||||
13,
|
|
||||||
5,
|
|
||||||
28,
|
|
||||||
20,
|
|
||||||
12,
|
|
||||||
4
|
|
||||||
];
|
|
||||||
const PC2 = [
|
|
||||||
14,
|
|
||||||
17,
|
|
||||||
11,
|
|
||||||
24,
|
|
||||||
1,
|
|
||||||
5,
|
|
||||||
3,
|
|
||||||
28,
|
|
||||||
15,
|
|
||||||
6,
|
|
||||||
21,
|
|
||||||
10,
|
|
||||||
23,
|
|
||||||
19,
|
|
||||||
12,
|
|
||||||
4,
|
|
||||||
26,
|
|
||||||
8,
|
|
||||||
16,
|
|
||||||
7,
|
|
||||||
27,
|
|
||||||
20,
|
|
||||||
13,
|
|
||||||
2,
|
|
||||||
41,
|
|
||||||
52,
|
|
||||||
31,
|
|
||||||
37,
|
|
||||||
47,
|
|
||||||
55,
|
|
||||||
30,
|
|
||||||
40,
|
|
||||||
51,
|
|
||||||
45,
|
|
||||||
33,
|
|
||||||
48,
|
|
||||||
44,
|
|
||||||
49,
|
|
||||||
39,
|
|
||||||
56,
|
|
||||||
34,
|
|
||||||
53,
|
|
||||||
46,
|
|
||||||
42,
|
|
||||||
50,
|
|
||||||
36,
|
|
||||||
29,
|
|
||||||
32
|
|
||||||
];
|
|
||||||
const BIT_SHIFTS = [
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
4,
|
|
||||||
6,
|
|
||||||
8,
|
|
||||||
10,
|
|
||||||
12,
|
|
||||||
14,
|
|
||||||
15,
|
|
||||||
17,
|
|
||||||
19,
|
|
||||||
21,
|
|
||||||
23,
|
|
||||||
25,
|
|
||||||
27,
|
|
||||||
28
|
|
||||||
];
|
|
||||||
const SBOX_P = [
|
|
||||||
{
|
|
||||||
0: 8421888,
|
|
||||||
268435456: 32768,
|
|
||||||
536870912: 8421378,
|
|
||||||
805306368: 2,
|
|
||||||
1073741824: 512,
|
|
||||||
1342177280: 8421890,
|
|
||||||
1610612736: 8389122,
|
|
||||||
1879048192: 8388608,
|
|
||||||
2147483648: 514,
|
|
||||||
2415919104: 8389120,
|
|
||||||
2684354560: 33280,
|
|
||||||
2952790016: 8421376,
|
|
||||||
3221225472: 32770,
|
|
||||||
3489660928: 8388610,
|
|
||||||
3758096384: 0,
|
|
||||||
4026531840: 33282,
|
|
||||||
134217728: 0,
|
|
||||||
402653184: 8421890,
|
|
||||||
671088640: 33282,
|
|
||||||
939524096: 32768,
|
|
||||||
1207959552: 8421888,
|
|
||||||
1476395008: 512,
|
|
||||||
1744830464: 8421378,
|
|
||||||
2013265920: 2,
|
|
||||||
2281701376: 8389120,
|
|
||||||
2550136832: 33280,
|
|
||||||
2818572288: 8421376,
|
|
||||||
3087007744: 8389122,
|
|
||||||
3355443200: 8388610,
|
|
||||||
3623878656: 32770,
|
|
||||||
3892314112: 514,
|
|
||||||
4160749568: 8388608,
|
|
||||||
1: 32768,
|
|
||||||
268435457: 2,
|
|
||||||
536870913: 8421888,
|
|
||||||
805306369: 8388608,
|
|
||||||
1073741825: 8421378,
|
|
||||||
1342177281: 33280,
|
|
||||||
1610612737: 512,
|
|
||||||
1879048193: 8389122,
|
|
||||||
2147483649: 8421890,
|
|
||||||
2415919105: 8421376,
|
|
||||||
2684354561: 8388610,
|
|
||||||
2952790017: 33282,
|
|
||||||
3221225473: 514,
|
|
||||||
3489660929: 8389120,
|
|
||||||
3758096385: 32770,
|
|
||||||
4026531841: 0,
|
|
||||||
134217729: 8421890,
|
|
||||||
402653185: 8421376,
|
|
||||||
671088641: 8388608,
|
|
||||||
939524097: 512,
|
|
||||||
1207959553: 32768,
|
|
||||||
1476395009: 8388610,
|
|
||||||
1744830465: 2,
|
|
||||||
2013265921: 33282,
|
|
||||||
2281701377: 32770,
|
|
||||||
2550136833: 8389122,
|
|
||||||
2818572289: 514,
|
|
||||||
3087007745: 8421888,
|
|
||||||
3355443201: 8389120,
|
|
||||||
3623878657: 0,
|
|
||||||
3892314113: 33280,
|
|
||||||
4160749569: 8421378
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0: 1074282512,
|
|
||||||
16777216: 16384,
|
|
||||||
33554432: 524288,
|
|
||||||
50331648: 1074266128,
|
|
||||||
67108864: 1073741840,
|
|
||||||
83886080: 1074282496,
|
|
||||||
100663296: 1073758208,
|
|
||||||
117440512: 16,
|
|
||||||
134217728: 540672,
|
|
||||||
150994944: 1073758224,
|
|
||||||
167772160: 1073741824,
|
|
||||||
184549376: 540688,
|
|
||||||
201326592: 524304,
|
|
||||||
218103808: 0,
|
|
||||||
234881024: 16400,
|
|
||||||
251658240: 1074266112,
|
|
||||||
8388608: 1073758208,
|
|
||||||
25165824: 540688,
|
|
||||||
41943040: 16,
|
|
||||||
58720256: 1073758224,
|
|
||||||
75497472: 1074282512,
|
|
||||||
92274688: 1073741824,
|
|
||||||
109051904: 524288,
|
|
||||||
125829120: 1074266128,
|
|
||||||
142606336: 524304,
|
|
||||||
159383552: 0,
|
|
||||||
176160768: 16384,
|
|
||||||
192937984: 1074266112,
|
|
||||||
209715200: 1073741840,
|
|
||||||
226492416: 540672,
|
|
||||||
243269632: 1074282496,
|
|
||||||
260046848: 16400,
|
|
||||||
268435456: 0,
|
|
||||||
285212672: 1074266128,
|
|
||||||
301989888: 1073758224,
|
|
||||||
318767104: 1074282496,
|
|
||||||
335544320: 1074266112,
|
|
||||||
352321536: 16,
|
|
||||||
369098752: 540688,
|
|
||||||
385875968: 16384,
|
|
||||||
402653184: 16400,
|
|
||||||
419430400: 524288,
|
|
||||||
436207616: 524304,
|
|
||||||
452984832: 1073741840,
|
|
||||||
469762048: 540672,
|
|
||||||
486539264: 1073758208,
|
|
||||||
503316480: 1073741824,
|
|
||||||
520093696: 1074282512,
|
|
||||||
276824064: 540688,
|
|
||||||
293601280: 524288,
|
|
||||||
310378496: 1074266112,
|
|
||||||
327155712: 16384,
|
|
||||||
343932928: 1073758208,
|
|
||||||
360710144: 1074282512,
|
|
||||||
377487360: 16,
|
|
||||||
394264576: 1073741824,
|
|
||||||
411041792: 1074282496,
|
|
||||||
427819008: 1073741840,
|
|
||||||
444596224: 1073758224,
|
|
||||||
461373440: 524304,
|
|
||||||
478150656: 0,
|
|
||||||
494927872: 16400,
|
|
||||||
511705088: 1074266128,
|
|
||||||
528482304: 540672
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0: 260,
|
|
||||||
1048576: 0,
|
|
||||||
2097152: 67109120,
|
|
||||||
3145728: 65796,
|
|
||||||
4194304: 65540,
|
|
||||||
5242880: 67108868,
|
|
||||||
6291456: 67174660,
|
|
||||||
7340032: 67174400,
|
|
||||||
8388608: 67108864,
|
|
||||||
9437184: 67174656,
|
|
||||||
10485760: 65792,
|
|
||||||
11534336: 67174404,
|
|
||||||
12582912: 67109124,
|
|
||||||
13631488: 65536,
|
|
||||||
14680064: 4,
|
|
||||||
15728640: 256,
|
|
||||||
524288: 67174656,
|
|
||||||
1572864: 67174404,
|
|
||||||
2621440: 0,
|
|
||||||
3670016: 67109120,
|
|
||||||
4718592: 67108868,
|
|
||||||
5767168: 65536,
|
|
||||||
6815744: 65540,
|
|
||||||
7864320: 260,
|
|
||||||
8912896: 4,
|
|
||||||
9961472: 256,
|
|
||||||
11010048: 67174400,
|
|
||||||
12058624: 65796,
|
|
||||||
13107200: 65792,
|
|
||||||
14155776: 67109124,
|
|
||||||
15204352: 67174660,
|
|
||||||
16252928: 67108864,
|
|
||||||
16777216: 67174656,
|
|
||||||
17825792: 65540,
|
|
||||||
18874368: 65536,
|
|
||||||
19922944: 67109120,
|
|
||||||
20971520: 256,
|
|
||||||
22020096: 67174660,
|
|
||||||
23068672: 67108868,
|
|
||||||
24117248: 0,
|
|
||||||
25165824: 67109124,
|
|
||||||
26214400: 67108864,
|
|
||||||
27262976: 4,
|
|
||||||
28311552: 65792,
|
|
||||||
29360128: 67174400,
|
|
||||||
30408704: 260,
|
|
||||||
31457280: 65796,
|
|
||||||
32505856: 67174404,
|
|
||||||
17301504: 67108864,
|
|
||||||
18350080: 260,
|
|
||||||
19398656: 67174656,
|
|
||||||
20447232: 0,
|
|
||||||
21495808: 65540,
|
|
||||||
22544384: 67109120,
|
|
||||||
23592960: 256,
|
|
||||||
24641536: 67174404,
|
|
||||||
25690112: 65536,
|
|
||||||
26738688: 67174660,
|
|
||||||
27787264: 65796,
|
|
||||||
28835840: 67108868,
|
|
||||||
29884416: 67109124,
|
|
||||||
30932992: 67174400,
|
|
||||||
31981568: 4,
|
|
||||||
33030144: 65792
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0: 2151682048,
|
|
||||||
65536: 2147487808,
|
|
||||||
131072: 4198464,
|
|
||||||
196608: 2151677952,
|
|
||||||
262144: 0,
|
|
||||||
327680: 4198400,
|
|
||||||
393216: 2147483712,
|
|
||||||
458752: 4194368,
|
|
||||||
524288: 2147483648,
|
|
||||||
589824: 4194304,
|
|
||||||
655360: 64,
|
|
||||||
720896: 2147487744,
|
|
||||||
786432: 2151678016,
|
|
||||||
851968: 4160,
|
|
||||||
917504: 4096,
|
|
||||||
983040: 2151682112,
|
|
||||||
32768: 2147487808,
|
|
||||||
98304: 64,
|
|
||||||
163840: 2151678016,
|
|
||||||
229376: 2147487744,
|
|
||||||
294912: 4198400,
|
|
||||||
360448: 2151682112,
|
|
||||||
425984: 0,
|
|
||||||
491520: 2151677952,
|
|
||||||
557056: 4096,
|
|
||||||
622592: 2151682048,
|
|
||||||
688128: 4194304,
|
|
||||||
753664: 4160,
|
|
||||||
819200: 2147483648,
|
|
||||||
884736: 4194368,
|
|
||||||
950272: 4198464,
|
|
||||||
1015808: 2147483712,
|
|
||||||
1048576: 4194368,
|
|
||||||
1114112: 4198400,
|
|
||||||
1179648: 2147483712,
|
|
||||||
1245184: 0,
|
|
||||||
1310720: 4160,
|
|
||||||
1376256: 2151678016,
|
|
||||||
1441792: 2151682048,
|
|
||||||
1507328: 2147487808,
|
|
||||||
1572864: 2151682112,
|
|
||||||
1638400: 2147483648,
|
|
||||||
1703936: 2151677952,
|
|
||||||
1769472: 4198464,
|
|
||||||
1835008: 2147487744,
|
|
||||||
1900544: 4194304,
|
|
||||||
1966080: 64,
|
|
||||||
2031616: 4096,
|
|
||||||
1081344: 2151677952,
|
|
||||||
1146880: 2151682112,
|
|
||||||
1212416: 0,
|
|
||||||
1277952: 4198400,
|
|
||||||
1343488: 4194368,
|
|
||||||
1409024: 2147483648,
|
|
||||||
1474560: 2147487808,
|
|
||||||
1540096: 64,
|
|
||||||
1605632: 2147483712,
|
|
||||||
1671168: 4096,
|
|
||||||
1736704: 2147487744,
|
|
||||||
1802240: 2151678016,
|
|
||||||
1867776: 4160,
|
|
||||||
1933312: 2151682048,
|
|
||||||
1998848: 4194304,
|
|
||||||
2064384: 4198464
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0: 128,
|
|
||||||
4096: 17039360,
|
|
||||||
8192: 262144,
|
|
||||||
12288: 536870912,
|
|
||||||
16384: 537133184,
|
|
||||||
20480: 16777344,
|
|
||||||
24576: 553648256,
|
|
||||||
28672: 262272,
|
|
||||||
32768: 16777216,
|
|
||||||
36864: 537133056,
|
|
||||||
40960: 536871040,
|
|
||||||
45056: 553910400,
|
|
||||||
49152: 553910272,
|
|
||||||
53248: 0,
|
|
||||||
57344: 17039488,
|
|
||||||
61440: 553648128,
|
|
||||||
2048: 17039488,
|
|
||||||
6144: 553648256,
|
|
||||||
10240: 128,
|
|
||||||
14336: 17039360,
|
|
||||||
18432: 262144,
|
|
||||||
22528: 537133184,
|
|
||||||
26624: 553910272,
|
|
||||||
30720: 536870912,
|
|
||||||
34816: 537133056,
|
|
||||||
38912: 0,
|
|
||||||
43008: 553910400,
|
|
||||||
47104: 16777344,
|
|
||||||
51200: 536871040,
|
|
||||||
55296: 553648128,
|
|
||||||
59392: 16777216,
|
|
||||||
63488: 262272,
|
|
||||||
65536: 262144,
|
|
||||||
69632: 128,
|
|
||||||
73728: 536870912,
|
|
||||||
77824: 553648256,
|
|
||||||
81920: 16777344,
|
|
||||||
86016: 553910272,
|
|
||||||
90112: 537133184,
|
|
||||||
94208: 16777216,
|
|
||||||
98304: 553910400,
|
|
||||||
102400: 553648128,
|
|
||||||
106496: 17039360,
|
|
||||||
110592: 537133056,
|
|
||||||
114688: 262272,
|
|
||||||
118784: 536871040,
|
|
||||||
122880: 0,
|
|
||||||
126976: 17039488,
|
|
||||||
67584: 553648256,
|
|
||||||
71680: 16777216,
|
|
||||||
75776: 17039360,
|
|
||||||
79872: 537133184,
|
|
||||||
83968: 536870912,
|
|
||||||
88064: 17039488,
|
|
||||||
92160: 128,
|
|
||||||
96256: 553910272,
|
|
||||||
100352: 262272,
|
|
||||||
104448: 553910400,
|
|
||||||
108544: 0,
|
|
||||||
112640: 553648128,
|
|
||||||
116736: 16777344,
|
|
||||||
120832: 262144,
|
|
||||||
124928: 537133056,
|
|
||||||
129024: 536871040
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0: 268435464,
|
|
||||||
256: 8192,
|
|
||||||
512: 270532608,
|
|
||||||
768: 270540808,
|
|
||||||
1024: 268443648,
|
|
||||||
1280: 2097152,
|
|
||||||
1536: 2097160,
|
|
||||||
1792: 268435456,
|
|
||||||
2048: 0,
|
|
||||||
2304: 268443656,
|
|
||||||
2560: 2105344,
|
|
||||||
2816: 8,
|
|
||||||
3072: 270532616,
|
|
||||||
3328: 2105352,
|
|
||||||
3584: 8200,
|
|
||||||
3840: 270540800,
|
|
||||||
128: 270532608,
|
|
||||||
384: 270540808,
|
|
||||||
640: 8,
|
|
||||||
896: 2097152,
|
|
||||||
1152: 2105352,
|
|
||||||
1408: 268435464,
|
|
||||||
1664: 268443648,
|
|
||||||
1920: 8200,
|
|
||||||
2176: 2097160,
|
|
||||||
2432: 8192,
|
|
||||||
2688: 268443656,
|
|
||||||
2944: 270532616,
|
|
||||||
3200: 0,
|
|
||||||
3456: 270540800,
|
|
||||||
3712: 2105344,
|
|
||||||
3968: 268435456,
|
|
||||||
4096: 268443648,
|
|
||||||
4352: 270532616,
|
|
||||||
4608: 270540808,
|
|
||||||
4864: 8200,
|
|
||||||
5120: 2097152,
|
|
||||||
5376: 268435456,
|
|
||||||
5632: 268435464,
|
|
||||||
5888: 2105344,
|
|
||||||
6144: 2105352,
|
|
||||||
6400: 0,
|
|
||||||
6656: 8,
|
|
||||||
6912: 270532608,
|
|
||||||
7168: 8192,
|
|
||||||
7424: 268443656,
|
|
||||||
7680: 270540800,
|
|
||||||
7936: 2097160,
|
|
||||||
4224: 8,
|
|
||||||
4480: 2105344,
|
|
||||||
4736: 2097152,
|
|
||||||
4992: 268435464,
|
|
||||||
5248: 268443648,
|
|
||||||
5504: 8200,
|
|
||||||
5760: 270540808,
|
|
||||||
6016: 270532608,
|
|
||||||
6272: 270540800,
|
|
||||||
6528: 270532616,
|
|
||||||
6784: 8192,
|
|
||||||
7040: 2105352,
|
|
||||||
7296: 2097160,
|
|
||||||
7552: 0,
|
|
||||||
7808: 268435456,
|
|
||||||
8064: 268443656
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0: 1048576,
|
|
||||||
16: 33555457,
|
|
||||||
32: 1024,
|
|
||||||
48: 1049601,
|
|
||||||
64: 34604033,
|
|
||||||
80: 0,
|
|
||||||
96: 1,
|
|
||||||
112: 34603009,
|
|
||||||
128: 33555456,
|
|
||||||
144: 1048577,
|
|
||||||
160: 33554433,
|
|
||||||
176: 34604032,
|
|
||||||
192: 34603008,
|
|
||||||
208: 1025,
|
|
||||||
224: 1049600,
|
|
||||||
240: 33554432,
|
|
||||||
8: 34603009,
|
|
||||||
24: 0,
|
|
||||||
40: 33555457,
|
|
||||||
56: 34604032,
|
|
||||||
72: 1048576,
|
|
||||||
88: 33554433,
|
|
||||||
104: 33554432,
|
|
||||||
120: 1025,
|
|
||||||
136: 1049601,
|
|
||||||
152: 33555456,
|
|
||||||
168: 34603008,
|
|
||||||
184: 1048577,
|
|
||||||
200: 1024,
|
|
||||||
216: 34604033,
|
|
||||||
232: 1,
|
|
||||||
248: 1049600,
|
|
||||||
256: 33554432,
|
|
||||||
272: 1048576,
|
|
||||||
288: 33555457,
|
|
||||||
304: 34603009,
|
|
||||||
320: 1048577,
|
|
||||||
336: 33555456,
|
|
||||||
352: 34604032,
|
|
||||||
368: 1049601,
|
|
||||||
384: 1025,
|
|
||||||
400: 34604033,
|
|
||||||
416: 1049600,
|
|
||||||
432: 1,
|
|
||||||
448: 0,
|
|
||||||
464: 34603008,
|
|
||||||
480: 33554433,
|
|
||||||
496: 1024,
|
|
||||||
264: 1049600,
|
|
||||||
280: 33555457,
|
|
||||||
296: 34603009,
|
|
||||||
312: 1,
|
|
||||||
328: 33554432,
|
|
||||||
344: 1048576,
|
|
||||||
360: 1025,
|
|
||||||
376: 34604032,
|
|
||||||
392: 33554433,
|
|
||||||
408: 34603008,
|
|
||||||
424: 0,
|
|
||||||
440: 34604033,
|
|
||||||
456: 1049601,
|
|
||||||
472: 1024,
|
|
||||||
488: 33555456,
|
|
||||||
504: 1048577
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0: 134219808,
|
|
||||||
1: 131072,
|
|
||||||
2: 134217728,
|
|
||||||
3: 32,
|
|
||||||
4: 131104,
|
|
||||||
5: 134350880,
|
|
||||||
6: 134350848,
|
|
||||||
7: 2048,
|
|
||||||
8: 134348800,
|
|
||||||
9: 134219776,
|
|
||||||
10: 133120,
|
|
||||||
11: 134348832,
|
|
||||||
12: 2080,
|
|
||||||
13: 0,
|
|
||||||
14: 134217760,
|
|
||||||
15: 133152,
|
|
||||||
2147483648: 2048,
|
|
||||||
2147483649: 134350880,
|
|
||||||
2147483650: 134219808,
|
|
||||||
2147483651: 134217728,
|
|
||||||
2147483652: 134348800,
|
|
||||||
2147483653: 133120,
|
|
||||||
2147483654: 133152,
|
|
||||||
2147483655: 32,
|
|
||||||
2147483656: 134217760,
|
|
||||||
2147483657: 2080,
|
|
||||||
2147483658: 131104,
|
|
||||||
2147483659: 134350848,
|
|
||||||
2147483660: 0,
|
|
||||||
2147483661: 134348832,
|
|
||||||
2147483662: 134219776,
|
|
||||||
2147483663: 131072,
|
|
||||||
16: 133152,
|
|
||||||
17: 134350848,
|
|
||||||
18: 32,
|
|
||||||
19: 2048,
|
|
||||||
20: 134219776,
|
|
||||||
21: 134217760,
|
|
||||||
22: 134348832,
|
|
||||||
23: 131072,
|
|
||||||
24: 0,
|
|
||||||
25: 131104,
|
|
||||||
26: 134348800,
|
|
||||||
27: 134219808,
|
|
||||||
28: 134350880,
|
|
||||||
29: 133120,
|
|
||||||
30: 2080,
|
|
||||||
31: 134217728,
|
|
||||||
2147483664: 131072,
|
|
||||||
2147483665: 2048,
|
|
||||||
2147483666: 134348832,
|
|
||||||
2147483667: 133152,
|
|
||||||
2147483668: 32,
|
|
||||||
2147483669: 134348800,
|
|
||||||
2147483670: 134217728,
|
|
||||||
2147483671: 134219808,
|
|
||||||
2147483672: 134350880,
|
|
||||||
2147483673: 134217760,
|
|
||||||
2147483674: 134219776,
|
|
||||||
2147483675: 0,
|
|
||||||
2147483676: 133120,
|
|
||||||
2147483677: 2080,
|
|
||||||
2147483678: 131104,
|
|
||||||
2147483679: 134350848
|
|
||||||
}
|
|
||||||
];
|
|
||||||
const SBOX_MASK = [
|
|
||||||
4160749569,
|
|
||||||
528482304,
|
|
||||||
33030144,
|
|
||||||
2064384,
|
|
||||||
129024,
|
|
||||||
8064,
|
|
||||||
504,
|
|
||||||
2147483679
|
|
||||||
];
|
|
||||||
function exchangeLR(offset, mask) {
|
|
||||||
if (this._lBlock === void 0 || this._rBlock === void 0) throw new Error("Block values not initialized");
|
|
||||||
const t = (this._lBlock >>> offset ^ this._rBlock) & mask;
|
|
||||||
this._rBlock ^= t;
|
|
||||||
this._lBlock ^= t << offset;
|
|
||||||
}
|
|
||||||
function exchangeRL(offset, mask) {
|
|
||||||
if (this._lBlock === void 0 || this._rBlock === void 0) throw new Error("Block values not initialized");
|
|
||||||
const t = (this._rBlock >>> offset ^ this._lBlock) & mask;
|
|
||||||
this._lBlock ^= t;
|
|
||||||
this._rBlock ^= t << offset;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* DES block cipher algorithm.
|
|
||||||
*/
|
|
||||||
var DESAlgo = class extends BlockCipher {
|
|
||||||
/** Key size in 32-bit words */
|
|
||||||
static keySize = 64 / 32;
|
|
||||||
/** IV size in 32-bit words */
|
|
||||||
static ivSize = 64 / 32;
|
|
||||||
/** Subkeys for encryption */
|
|
||||||
_subKeys;
|
|
||||||
/** Inverse subkeys for decryption */
|
|
||||||
_invSubKeys;
|
|
||||||
/** Left block for processing */
|
|
||||||
_lBlock;
|
|
||||||
/** Right block for processing */
|
|
||||||
_rBlock;
|
|
||||||
constructor(xformMode, key, cfg) {
|
|
||||||
super(xformMode, key, cfg);
|
|
||||||
this.blockSize = 64 / 32;
|
|
||||||
}
|
|
||||||
_doReset() {
|
|
||||||
const key = this._key;
|
|
||||||
const keyWords = key.words;
|
|
||||||
const keyBits = [];
|
|
||||||
for (let i = 0; i < 56; i += 1) {
|
|
||||||
const keyBitPos = PC1[i] - 1;
|
|
||||||
keyBits[i] = keyWords[keyBitPos >>> 5] >>> 31 - keyBitPos % 32 & 1;
|
|
||||||
}
|
|
||||||
this._subKeys = [];
|
|
||||||
const subKeys = this._subKeys;
|
|
||||||
for (let nSubKey = 0; nSubKey < 16; nSubKey += 1) {
|
|
||||||
subKeys[nSubKey] = [];
|
|
||||||
const subKey = subKeys[nSubKey];
|
|
||||||
const bitShift = BIT_SHIFTS[nSubKey];
|
|
||||||
for (let i = 0; i < 24; i += 1) {
|
|
||||||
subKey[i / 6 | 0] |= keyBits[(PC2[i] - 1 + bitShift) % 28] << 31 - i % 6;
|
|
||||||
subKey[4 + (i / 6 | 0)] |= keyBits[28 + (PC2[i + 24] - 1 + bitShift) % 28] << 31 - i % 6;
|
|
||||||
}
|
|
||||||
subKey[0] = subKey[0] << 1 | subKey[0] >>> 31;
|
|
||||||
for (let i = 1; i < 7; i += 1) subKey[i] >>>= (i - 1) * 4 + 3;
|
|
||||||
subKey[7] = subKey[7] << 5 | subKey[7] >>> 27;
|
|
||||||
}
|
|
||||||
this._invSubKeys = [];
|
|
||||||
const invSubKeys = this._invSubKeys;
|
|
||||||
for (let i = 0; i < 16; i += 1) invSubKeys[i] = subKeys[15 - i];
|
|
||||||
}
|
|
||||||
encryptBlock(M, offset) {
|
|
||||||
this._doCryptBlock(M, offset, this._subKeys);
|
|
||||||
}
|
|
||||||
decryptBlock(M, offset) {
|
|
||||||
this._doCryptBlock(M, offset, this._invSubKeys);
|
|
||||||
}
|
|
||||||
_doCryptBlock(M, offset, subKeys) {
|
|
||||||
const _M = M;
|
|
||||||
this._lBlock = M[offset];
|
|
||||||
this._rBlock = M[offset + 1];
|
|
||||||
exchangeLR.call(this, 4, 252645135);
|
|
||||||
exchangeLR.call(this, 16, 65535);
|
|
||||||
exchangeRL.call(this, 2, 858993459);
|
|
||||||
exchangeRL.call(this, 8, 16711935);
|
|
||||||
exchangeLR.call(this, 1, 1431655765);
|
|
||||||
for (let round = 0; round < 16; round += 1) {
|
|
||||||
const subKey = subKeys[round];
|
|
||||||
const lBlock = this._lBlock;
|
|
||||||
const rBlock = this._rBlock;
|
|
||||||
let f = 0;
|
|
||||||
for (let i = 0; i < 8; i += 1) f |= SBOX_P[i][((rBlock ^ subKey[i]) & SBOX_MASK[i]) >>> 0];
|
|
||||||
this._lBlock = rBlock;
|
|
||||||
this._rBlock = lBlock ^ f;
|
|
||||||
}
|
|
||||||
const t = this._lBlock;
|
|
||||||
this._lBlock = this._rBlock;
|
|
||||||
this._rBlock = t;
|
|
||||||
exchangeLR.call(this, 1, 1431655765);
|
|
||||||
exchangeRL.call(this, 8, 16711935);
|
|
||||||
exchangeRL.call(this, 2, 858993459);
|
|
||||||
exchangeLR.call(this, 16, 65535);
|
|
||||||
exchangeLR.call(this, 4, 252645135);
|
|
||||||
_M[offset] = this._lBlock;
|
|
||||||
_M[offset + 1] = this._rBlock;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Shortcut functions to the cipher's object interface.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* var ciphertext = DES.encrypt(message, key, cfg);
|
|
||||||
* var plaintext = DES.decrypt(ciphertext, key, cfg);
|
|
||||||
*/
|
|
||||||
const DES = BlockCipher._createHelper(DESAlgo);
|
|
||||||
/**
|
|
||||||
* Triple-DES block cipher algorithm.
|
|
||||||
*/
|
|
||||||
var TripleDESAlgo = class extends BlockCipher {
|
|
||||||
/** Key size in 32-bit words */
|
|
||||||
static keySize = 192 / 32;
|
|
||||||
/** IV size in 32-bit words */
|
|
||||||
static ivSize = 64 / 32;
|
|
||||||
/** First DES instance */
|
|
||||||
_des1;
|
|
||||||
/** Second DES instance */
|
|
||||||
_des2;
|
|
||||||
/** Third DES instance */
|
|
||||||
_des3;
|
|
||||||
_doReset() {
|
|
||||||
const key = this._key;
|
|
||||||
const keyWords = key.words;
|
|
||||||
if (keyWords.length !== 2 && keyWords.length !== 4 && keyWords.length < 6) throw new Error("Invalid key length - 3DES requires the key length to be 64, 128, 192 or >192.");
|
|
||||||
const key1 = keyWords.slice(0, 2);
|
|
||||||
const key2 = keyWords.length < 4 ? keyWords.slice(0, 2) : keyWords.slice(2, 4);
|
|
||||||
const key3 = keyWords.length < 6 ? keyWords.slice(0, 2) : keyWords.slice(4, 6);
|
|
||||||
this._des1 = DESAlgo.createEncryptor(WordArray.create(key1));
|
|
||||||
this._des2 = DESAlgo.createEncryptor(WordArray.create(key2));
|
|
||||||
this._des3 = DESAlgo.createEncryptor(WordArray.create(key3));
|
|
||||||
}
|
|
||||||
encryptBlock(M, offset) {
|
|
||||||
this._des1.encryptBlock(M, offset);
|
|
||||||
this._des2.decryptBlock(M, offset);
|
|
||||||
this._des3.encryptBlock(M, offset);
|
|
||||||
}
|
|
||||||
decryptBlock(M, offset) {
|
|
||||||
this._des3.decryptBlock(M, offset);
|
|
||||||
this._des2.encryptBlock(M, offset);
|
|
||||||
this._des1.decryptBlock(M, offset);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Shortcut functions to the cipher's object interface.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* var ciphertext = TripleDES.encrypt(message, key, cfg);
|
|
||||||
* var plaintext = TripleDES.decrypt(ciphertext, key, cfg);
|
|
||||||
*/
|
|
||||||
const TripleDES = BlockCipher._createHelper(TripleDESAlgo);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { DES, DESAlgo, TripleDES, TripleDESAlgo };
|
|
||||||
//# sourceMappingURL=tripledes.mjs.map
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
import { Base, WordArray } from "./core.mjs";
|
|
||||||
|
|
||||||
//#region src/x64-core.ts
|
|
||||||
/**
|
|
||||||
* A 64-bit word representation.
|
|
||||||
* Stores a 64-bit value as two 32-bit words due to JavaScript's number limitations.
|
|
||||||
*
|
|
||||||
* @property high - The high 32 bits
|
|
||||||
* @property low - The low 32 bits
|
|
||||||
*/
|
|
||||||
var X64Word = class extends Base {
|
|
||||||
/** The high 32 bits of the 64-bit word */
|
|
||||||
high;
|
|
||||||
/** The low 32 bits of the 64-bit word */
|
|
||||||
low;
|
|
||||||
/**
|
|
||||||
* Initializes a newly created 64-bit word.
|
|
||||||
*
|
|
||||||
* @param high - The high 32 bits (default: 0)
|
|
||||||
* @param low - The low 32 bits (default: 0)
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const x64Word = new X64Word(0x00010203, 0x04050607);
|
|
||||||
* const x64Word = X64Word.create(0x00010203, 0x04050607);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
constructor(high = 0, low = 0) {
|
|
||||||
super();
|
|
||||||
this.high = high;
|
|
||||||
this.low = low;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Creates a copy of this word.
|
|
||||||
*
|
|
||||||
* @returns The cloned 64-bit word
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const clone = x64Word.clone();
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
clone() {
|
|
||||||
const clone = super.clone();
|
|
||||||
clone.high = this.high;
|
|
||||||
clone.low = this.low;
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* An array of 64-bit words.
|
|
||||||
* This is used for algorithms that operate on 64-bit words, such as SHA-512.
|
|
||||||
*
|
|
||||||
* @property words - The array of X64Word objects
|
|
||||||
* @property sigBytes - The number of significant bytes in this word array
|
|
||||||
*/
|
|
||||||
var X64WordArray = class X64WordArray extends Base {
|
|
||||||
/** The array of X64Word objects */
|
|
||||||
words;
|
|
||||||
/** The number of significant bytes in this word array */
|
|
||||||
sigBytes;
|
|
||||||
/**
|
|
||||||
* Initializes a newly created 64-bit word array.
|
|
||||||
*
|
|
||||||
* @param words - An array of X64Word objects
|
|
||||||
* @param sigBytes - The number of significant bytes in the words (defaults to words.length * 8)
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const wordArray = new X64WordArray();
|
|
||||||
*
|
|
||||||
* const wordArray = new X64WordArray([
|
|
||||||
* new X64Word(0x00010203, 0x04050607),
|
|
||||||
* new X64Word(0x18191a1b, 0x1c1d1e1f)
|
|
||||||
* ]);
|
|
||||||
*
|
|
||||||
* const wordArray = new X64WordArray([
|
|
||||||
* new X64Word(0x00010203, 0x04050607),
|
|
||||||
* new X64Word(0x18191a1b, 0x1c1d1e1f)
|
|
||||||
* ], 10);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
constructor(words = [], sigBytes = words.length * 8) {
|
|
||||||
super();
|
|
||||||
this.words = words;
|
|
||||||
this.sigBytes = sigBytes;
|
|
||||||
}
|
|
||||||
static create(...args) {
|
|
||||||
const [words, sigBytes] = args;
|
|
||||||
return new X64WordArray(words, sigBytes);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Converts this 64-bit word array to a 32-bit word array.
|
|
||||||
* Each 64-bit word is split into two 32-bit words (high and low).
|
|
||||||
*
|
|
||||||
* @returns This word array's data as a 32-bit word array
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const x32WordArray = x64WordArray.toX32();
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
toX32() {
|
|
||||||
const x64Words = this.words;
|
|
||||||
const x64WordsLength = x64Words.length;
|
|
||||||
const x32Words = [];
|
|
||||||
for (let i = 0; i < x64WordsLength; i += 1) {
|
|
||||||
const x64Word = x64Words[i];
|
|
||||||
x32Words.push(x64Word.high);
|
|
||||||
x32Words.push(x64Word.low);
|
|
||||||
}
|
|
||||||
return WordArray.create(x32Words, this.sigBytes);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Creates a deep copy of this word array.
|
|
||||||
* Clones both the array and each X64Word object within it.
|
|
||||||
*
|
|
||||||
* @returns The cloned X64WordArray
|
|
||||||
* @example
|
|
||||||
* ```javascript
|
|
||||||
* const clone = x64WordArray.clone();
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
clone() {
|
|
||||||
const clone = super.clone();
|
|
||||||
clone.words = this.words.slice(0);
|
|
||||||
const { words } = clone;
|
|
||||||
const wordsLength = words.length;
|
|
||||||
for (let i = 0; i < wordsLength; i += 1) words[i] = words[i].clone();
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
export { X64Word, X64WordArray };
|
|
||||||
//# sourceMappingURL=x64-core.mjs.map
|
|
||||||
2
client/static/jquery-3.7.1.min.js
vendored
2
client/static/jquery-3.7.1.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,5 +0,0 @@
|
|||||||
/* esm.sh - react-dom@18.3.1 */
|
|
||||||
import "./react@18.3.1/react.mjs";
|
|
||||||
import "./scheduler@0.23.2/scheduler.mjs";
|
|
||||||
export * from "./react-dom@18.3.1/react-dom.mjs";
|
|
||||||
export { default } from "./react-dom@18.3.1/react-dom.mjs";
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,3 +0,0 @@
|
|||||||
/* esm.sh - react@18.3.1 */
|
|
||||||
export * from "./react@18.3.1/react.mjs";
|
|
||||||
export { default } from "./react@18.3.1/react.mjs";
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3
client/static/splitjs-1.6.5.min.js
vendored
3
client/static/splitjs-1.6.5.min.js
vendored
File diff suppressed because one or more lines are too long
73
client/style.css
Normal file
73
client/style.css
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/* 滑条*/
|
||||||
|
.no-scroll-bar::-webkit-scrollbar {
|
||||||
|
width: 0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* https://blog.csdn.net/qq_39347364/article/details/111996581*/
|
||||||
|
*::-webkit-scrollbar {
|
||||||
|
width: 7px;
|
||||||
|
height: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar-track {
|
||||||
|
width: 6px;
|
||||||
|
background: rgba(#101f1c, 0.1);
|
||||||
|
-webkit-border-radius: 2em;
|
||||||
|
-moz-border-radius: 2em;
|
||||||
|
border-radius: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar-thumb {
|
||||||
|
background-color: rgba(144, 147, 153, 0.5);
|
||||||
|
background-clip: padding-box;
|
||||||
|
min-height: 28px;
|
||||||
|
-webkit-border-radius: 2em;
|
||||||
|
-moz-border-radius: 2em;
|
||||||
|
border-radius: 2em;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: rgba(144, 147, 153, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 使用系统字体 在部分系统表现很好*/
|
||||||
|
/* 我们至今仍未能知道桌面端浏览器字体的秘密*/
|
||||||
|
*:not(.material-icons, .mdui-icon, mdui-icon, .fa, .google-symbols) {
|
||||||
|
font-family: -apple-system, system-ui, -webkit-system-font !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
margin: 0 0 0 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
margin: 0 0 0 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 防止小尺寸图片模糊*/
|
||||||
|
* {
|
||||||
|
image-rendering: crisp-edges;
|
||||||
|
image-rendering: -moz-crisp-edges;
|
||||||
|
image-rendering: -o-crisp-edges;
|
||||||
|
image-rendering: -webkit-optimize-contrast;
|
||||||
|
-ms-interpolation-mode: nearest-neighbor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gutter {
|
||||||
|
background-color: rgb(var(--mdui-color-surface-variant));
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gutter.gutter-horizontal {
|
||||||
|
cursor: col-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgb(var(--mdui-color-primary));
|
||||||
|
}
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="zh-CN" class="mdui-theme-auto">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, shrink-to-fit=no" />
|
|
||||||
<meta name="renderer" content="webkit" />
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://unpkg.com/mdui@2/mdui.css">
|
|
||||||
<script src="https://unpkg.com/mdui@2/mdui.global.js"></script>
|
|
||||||
|
|
||||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
|
||||||
<script src="https://cdn.socket.io/4.8.1/socket.io.min.js"></script>
|
|
||||||
|
|
||||||
<title>TheWhiteSilk Debugger</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<mdui-button id="send">Send</mdui-button>
|
|
||||||
<mdui-text-field id="edittext" autosize></mdui-text-field>
|
|
||||||
<div id="out">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
const socket = io()
|
|
||||||
$('#edittext').val(`{
|
|
||||||
"method": "",
|
|
||||||
"args": {
|
|
||||||
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
$('#send').click(() => {
|
|
||||||
socket.emit("the_white_silk", JSON.parse($('#edittext').val()), (response) => {
|
|
||||||
$('#out').text(JSON.stringify(response))
|
|
||||||
});
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
18
client/tsconfig.json
Normal file
18
client/tsconfig.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"lib": [
|
||||||
|
"ES2022",
|
||||||
|
"DOM",
|
||||||
|
"DOM.Iterable"
|
||||||
|
],
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noEmit": true
|
||||||
|
}
|
||||||
|
}
|
||||||
21297
client/typedef/csstype@3.1.3.d.ts
vendored
21297
client/typedef/csstype@3.1.3.d.ts
vendored
File diff suppressed because it is too large
Load Diff
131
client/typedef/prop-types@15.7.15.d.ts
vendored
131
client/typedef/prop-types@15.7.15.d.ts
vendored
@@ -1,131 +0,0 @@
|
|||||||
// eslint-disable-next-line @definitelytyped/export-just-namespace
|
|
||||||
export = PropTypes;
|
|
||||||
|
|
||||||
declare namespace PropTypes {
|
|
||||||
type ReactComponentLike =
|
|
||||||
| string
|
|
||||||
| ((props: any) => any)
|
|
||||||
| (new(props: any, context: any) => any);
|
|
||||||
|
|
||||||
interface ReactElementLike {
|
|
||||||
type: ReactComponentLike;
|
|
||||||
props: unknown;
|
|
||||||
key: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ReactNodeArray extends Iterable<ReactNodeLike> {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal Use `Awaited<ReactNodeLike>` instead
|
|
||||||
*/
|
|
||||||
// Helper type to enable `Awaited<ReactNodeLike>`.
|
|
||||||
// Must be a copy of the non-thenables of `ReactNodeLike`.
|
|
||||||
type AwaitedReactNodeLike =
|
|
||||||
| ReactElementLike
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| bigint
|
|
||||||
| ReactNodeArray
|
|
||||||
| boolean
|
|
||||||
| null
|
|
||||||
| undefined;
|
|
||||||
|
|
||||||
type ReactNodeLike =
|
|
||||||
| ReactElementLike
|
|
||||||
| ReactNodeArray
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| bigint
|
|
||||||
| boolean
|
|
||||||
| null
|
|
||||||
| undefined
|
|
||||||
| Promise<AwaitedReactNodeLike>;
|
|
||||||
|
|
||||||
const nominalTypeHack: unique symbol;
|
|
||||||
|
|
||||||
type IsOptional<T> = undefined extends T ? true : false;
|
|
||||||
|
|
||||||
type RequiredKeys<V> = {
|
|
||||||
[K in keyof V]-?: Exclude<V[K], undefined> extends Validator<infer T> ? IsOptional<T> extends true ? never : K
|
|
||||||
: never;
|
|
||||||
}[keyof V];
|
|
||||||
type OptionalKeys<V> = Exclude<keyof V, RequiredKeys<V>>;
|
|
||||||
type InferPropsInner<V> = { [K in keyof V]-?: InferType<V[K]> };
|
|
||||||
|
|
||||||
interface Validator<T> {
|
|
||||||
(
|
|
||||||
props: { [key: string]: any },
|
|
||||||
propName: string,
|
|
||||||
componentName: string,
|
|
||||||
location: string,
|
|
||||||
propFullName: string,
|
|
||||||
): Error | null;
|
|
||||||
[nominalTypeHack]?: {
|
|
||||||
type: T;
|
|
||||||
} | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Requireable<T> extends Validator<T | undefined | null> {
|
|
||||||
isRequired: Validator<NonNullable<T>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
type ValidationMap<T> = { [K in keyof T]?: Validator<T[K]> };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Like {@link ValidationMap} but treats `undefined`, `null` and optional properties the same.
|
|
||||||
* This type is only added as a migration path in React 19 where this type was removed from React.
|
|
||||||
* Runtime and compile time types would mismatch since you could see `undefined` at runtime when your types don't expect this type.
|
|
||||||
*/
|
|
||||||
type WeakValidationMap<T> = {
|
|
||||||
[K in keyof T]?: null extends T[K] ? Validator<T[K] | null | undefined>
|
|
||||||
: undefined extends T[K] ? Validator<T[K] | null | undefined>
|
|
||||||
: Validator<T[K]>;
|
|
||||||
};
|
|
||||||
|
|
||||||
type InferType<V> = V extends Validator<infer T> ? T : any;
|
|
||||||
type InferProps<V> =
|
|
||||||
& InferPropsInner<Pick<V, RequiredKeys<V>>>
|
|
||||||
& Partial<InferPropsInner<Pick<V, OptionalKeys<V>>>>;
|
|
||||||
|
|
||||||
const any: Requireable<any>;
|
|
||||||
const array: Requireable<any[]>;
|
|
||||||
const bool: Requireable<boolean>;
|
|
||||||
const func: Requireable<(...args: any[]) => any>;
|
|
||||||
const number: Requireable<number>;
|
|
||||||
const object: Requireable<object>;
|
|
||||||
const string: Requireable<string>;
|
|
||||||
const node: Requireable<ReactNodeLike>;
|
|
||||||
const element: Requireable<ReactElementLike>;
|
|
||||||
const symbol: Requireable<symbol>;
|
|
||||||
const elementType: Requireable<ReactComponentLike>;
|
|
||||||
function instanceOf<T>(expectedClass: new(...args: any[]) => T): Requireable<T>;
|
|
||||||
function oneOf<T>(types: readonly T[]): Requireable<T>;
|
|
||||||
function oneOfType<T extends Validator<any>>(types: T[]): Requireable<NonNullable<InferType<T>>>;
|
|
||||||
function arrayOf<T>(type: Validator<T>): Requireable<T[]>;
|
|
||||||
function objectOf<T>(type: Validator<T>): Requireable<{ [K in keyof any]: T }>;
|
|
||||||
function shape<P extends ValidationMap<any>>(type: P): Requireable<InferProps<P>>;
|
|
||||||
function exact<P extends ValidationMap<any>>(type: P): Requireable<Required<InferProps<P>>>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assert that the values match with the type specs.
|
|
||||||
* Error messages are memorized and will only be shown once.
|
|
||||||
*
|
|
||||||
* @param typeSpecs Map of name to a ReactPropType
|
|
||||||
* @param values Runtime values that need to be type-checked
|
|
||||||
* @param location e.g. "prop", "context", "child context"
|
|
||||||
* @param componentName Name of the component for error messages
|
|
||||||
* @param getStack Returns the component stack
|
|
||||||
*/
|
|
||||||
function checkPropTypes(
|
|
||||||
typeSpecs: any,
|
|
||||||
values: any,
|
|
||||||
location: string,
|
|
||||||
componentName: string,
|
|
||||||
getStack?: () => any,
|
|
||||||
): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Only available if NODE_ENV=production
|
|
||||||
*/
|
|
||||||
function resetWarningCache(): void;
|
|
||||||
}
|
|
||||||
161
client/typedef/react@18.3.18-global.d.ts
vendored
161
client/typedef/react@18.3.18-global.d.ts
vendored
@@ -1,161 +0,0 @@
|
|||||||
/*
|
|
||||||
React projects that don't include the DOM library need these interfaces to compile.
|
|
||||||
React Native applications use React, but there is no DOM available. The JavaScript runtime
|
|
||||||
is ES6/ES2015 only. These definitions allow such projects to compile with only `--lib ES6`.
|
|
||||||
|
|
||||||
Warning: all of these interfaces are empty. If you want type definitions for various properties
|
|
||||||
(such as HTMLInputElement.prototype.value), you need to add `--lib DOM` (via command line or tsconfig.json).
|
|
||||||
*/
|
|
||||||
|
|
||||||
interface Event {}
|
|
||||||
interface AnimationEvent extends Event {}
|
|
||||||
interface ClipboardEvent extends Event {}
|
|
||||||
interface CompositionEvent extends Event {}
|
|
||||||
interface DragEvent extends Event {}
|
|
||||||
interface FocusEvent extends Event {}
|
|
||||||
interface InputEvent extends Event {}
|
|
||||||
interface KeyboardEvent extends Event {}
|
|
||||||
interface MouseEvent extends Event {}
|
|
||||||
interface TouchEvent extends Event {}
|
|
||||||
interface PointerEvent extends Event {}
|
|
||||||
interface ToggleEvent extends Event {}
|
|
||||||
interface TransitionEvent extends Event {}
|
|
||||||
interface UIEvent extends Event {}
|
|
||||||
interface WheelEvent extends Event {}
|
|
||||||
|
|
||||||
interface EventTarget {}
|
|
||||||
interface Document {}
|
|
||||||
interface DataTransfer {}
|
|
||||||
interface StyleMedia {}
|
|
||||||
|
|
||||||
interface Element {}
|
|
||||||
interface DocumentFragment {}
|
|
||||||
|
|
||||||
interface HTMLElement extends Element {}
|
|
||||||
interface HTMLAnchorElement extends HTMLElement {}
|
|
||||||
interface HTMLAreaElement extends HTMLElement {}
|
|
||||||
interface HTMLAudioElement extends HTMLElement {}
|
|
||||||
interface HTMLBaseElement extends HTMLElement {}
|
|
||||||
interface HTMLBodyElement extends HTMLElement {}
|
|
||||||
interface HTMLBRElement extends HTMLElement {}
|
|
||||||
interface HTMLButtonElement extends HTMLElement {}
|
|
||||||
interface HTMLCanvasElement extends HTMLElement {}
|
|
||||||
interface HTMLDataElement extends HTMLElement {}
|
|
||||||
interface HTMLDataListElement extends HTMLElement {}
|
|
||||||
interface HTMLDetailsElement extends HTMLElement {}
|
|
||||||
interface HTMLDialogElement extends HTMLElement {}
|
|
||||||
interface HTMLDivElement extends HTMLElement {}
|
|
||||||
interface HTMLDListElement extends HTMLElement {}
|
|
||||||
interface HTMLEmbedElement extends HTMLElement {}
|
|
||||||
interface HTMLFieldSetElement extends HTMLElement {}
|
|
||||||
interface HTMLFormElement extends HTMLElement {}
|
|
||||||
interface HTMLHeadingElement extends HTMLElement {}
|
|
||||||
interface HTMLHeadElement extends HTMLElement {}
|
|
||||||
interface HTMLHRElement extends HTMLElement {}
|
|
||||||
interface HTMLHtmlElement extends HTMLElement {}
|
|
||||||
interface HTMLIFrameElement extends HTMLElement {}
|
|
||||||
interface HTMLImageElement extends HTMLElement {}
|
|
||||||
interface HTMLInputElement extends HTMLElement {}
|
|
||||||
interface HTMLModElement extends HTMLElement {}
|
|
||||||
interface HTMLLabelElement extends HTMLElement {}
|
|
||||||
interface HTMLLegendElement extends HTMLElement {}
|
|
||||||
interface HTMLLIElement extends HTMLElement {}
|
|
||||||
interface HTMLLinkElement extends HTMLElement {}
|
|
||||||
interface HTMLMapElement extends HTMLElement {}
|
|
||||||
interface HTMLMetaElement extends HTMLElement {}
|
|
||||||
interface HTMLMeterElement extends HTMLElement {}
|
|
||||||
interface HTMLObjectElement extends HTMLElement {}
|
|
||||||
interface HTMLOListElement extends HTMLElement {}
|
|
||||||
interface HTMLOptGroupElement extends HTMLElement {}
|
|
||||||
interface HTMLOptionElement extends HTMLElement {}
|
|
||||||
interface HTMLOutputElement extends HTMLElement {}
|
|
||||||
interface HTMLParagraphElement extends HTMLElement {}
|
|
||||||
interface HTMLParamElement extends HTMLElement {}
|
|
||||||
interface HTMLPreElement extends HTMLElement {}
|
|
||||||
interface HTMLProgressElement extends HTMLElement {}
|
|
||||||
interface HTMLQuoteElement extends HTMLElement {}
|
|
||||||
interface HTMLSlotElement extends HTMLElement {}
|
|
||||||
interface HTMLScriptElement extends HTMLElement {}
|
|
||||||
interface HTMLSelectElement extends HTMLElement {}
|
|
||||||
interface HTMLSourceElement extends HTMLElement {}
|
|
||||||
interface HTMLSpanElement extends HTMLElement {}
|
|
||||||
interface HTMLStyleElement extends HTMLElement {}
|
|
||||||
interface HTMLTableElement extends HTMLElement {}
|
|
||||||
interface HTMLTableColElement extends HTMLElement {}
|
|
||||||
interface HTMLTableDataCellElement extends HTMLElement {}
|
|
||||||
interface HTMLTableHeaderCellElement extends HTMLElement {}
|
|
||||||
interface HTMLTableRowElement extends HTMLElement {}
|
|
||||||
interface HTMLTableSectionElement extends HTMLElement {}
|
|
||||||
interface HTMLTemplateElement extends HTMLElement {}
|
|
||||||
interface HTMLTextAreaElement extends HTMLElement {}
|
|
||||||
interface HTMLTimeElement extends HTMLElement {}
|
|
||||||
interface HTMLTitleElement extends HTMLElement {}
|
|
||||||
interface HTMLTrackElement extends HTMLElement {}
|
|
||||||
interface HTMLUListElement extends HTMLElement {}
|
|
||||||
interface HTMLVideoElement extends HTMLElement {}
|
|
||||||
interface HTMLWebViewElement extends HTMLElement {}
|
|
||||||
|
|
||||||
interface SVGElement extends Element {}
|
|
||||||
interface SVGSVGElement extends SVGElement {}
|
|
||||||
interface SVGCircleElement extends SVGElement {}
|
|
||||||
interface SVGClipPathElement extends SVGElement {}
|
|
||||||
interface SVGDefsElement extends SVGElement {}
|
|
||||||
interface SVGDescElement extends SVGElement {}
|
|
||||||
interface SVGEllipseElement extends SVGElement {}
|
|
||||||
interface SVGFEBlendElement extends SVGElement {}
|
|
||||||
interface SVGFEColorMatrixElement extends SVGElement {}
|
|
||||||
interface SVGFEComponentTransferElement extends SVGElement {}
|
|
||||||
interface SVGFECompositeElement extends SVGElement {}
|
|
||||||
interface SVGFEConvolveMatrixElement extends SVGElement {}
|
|
||||||
interface SVGFEDiffuseLightingElement extends SVGElement {}
|
|
||||||
interface SVGFEDisplacementMapElement extends SVGElement {}
|
|
||||||
interface SVGFEDistantLightElement extends SVGElement {}
|
|
||||||
interface SVGFEDropShadowElement extends SVGElement {}
|
|
||||||
interface SVGFEFloodElement extends SVGElement {}
|
|
||||||
interface SVGFEFuncAElement extends SVGElement {}
|
|
||||||
interface SVGFEFuncBElement extends SVGElement {}
|
|
||||||
interface SVGFEFuncGElement extends SVGElement {}
|
|
||||||
interface SVGFEFuncRElement extends SVGElement {}
|
|
||||||
interface SVGFEGaussianBlurElement extends SVGElement {}
|
|
||||||
interface SVGFEImageElement extends SVGElement {}
|
|
||||||
interface SVGFEMergeElement extends SVGElement {}
|
|
||||||
interface SVGFEMergeNodeElement extends SVGElement {}
|
|
||||||
interface SVGFEMorphologyElement extends SVGElement {}
|
|
||||||
interface SVGFEOffsetElement extends SVGElement {}
|
|
||||||
interface SVGFEPointLightElement extends SVGElement {}
|
|
||||||
interface SVGFESpecularLightingElement extends SVGElement {}
|
|
||||||
interface SVGFESpotLightElement extends SVGElement {}
|
|
||||||
interface SVGFETileElement extends SVGElement {}
|
|
||||||
interface SVGFETurbulenceElement extends SVGElement {}
|
|
||||||
interface SVGFilterElement extends SVGElement {}
|
|
||||||
interface SVGForeignObjectElement extends SVGElement {}
|
|
||||||
interface SVGGElement extends SVGElement {}
|
|
||||||
interface SVGImageElement extends SVGElement {}
|
|
||||||
interface SVGLineElement extends SVGElement {}
|
|
||||||
interface SVGLinearGradientElement extends SVGElement {}
|
|
||||||
interface SVGMarkerElement extends SVGElement {}
|
|
||||||
interface SVGMaskElement extends SVGElement {}
|
|
||||||
interface SVGMetadataElement extends SVGElement {}
|
|
||||||
interface SVGPathElement extends SVGElement {}
|
|
||||||
interface SVGPatternElement extends SVGElement {}
|
|
||||||
interface SVGPolygonElement extends SVGElement {}
|
|
||||||
interface SVGPolylineElement extends SVGElement {}
|
|
||||||
interface SVGRadialGradientElement extends SVGElement {}
|
|
||||||
interface SVGRectElement extends SVGElement {}
|
|
||||||
interface SVGSetElement extends SVGElement {}
|
|
||||||
interface SVGStopElement extends SVGElement {}
|
|
||||||
interface SVGSwitchElement extends SVGElement {}
|
|
||||||
interface SVGSymbolElement extends SVGElement {}
|
|
||||||
interface SVGTextElement extends SVGElement {}
|
|
||||||
interface SVGTextPathElement extends SVGElement {}
|
|
||||||
interface SVGTSpanElement extends SVGElement {}
|
|
||||||
interface SVGUseElement extends SVGElement {}
|
|
||||||
interface SVGViewElement extends SVGElement {}
|
|
||||||
|
|
||||||
interface FormData {}
|
|
||||||
interface Text {}
|
|
||||||
interface TouchList {}
|
|
||||||
interface WebGLRenderingContext {}
|
|
||||||
interface WebGL2RenderingContext {}
|
|
||||||
|
|
||||||
interface TrustedHTML {}
|
|
||||||
4586
client/typedef/react@18.3.18.d.ts
vendored
4586
client/typedef/react@18.3.18.d.ts
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,169 +0,0 @@
|
|||||||
import Client from "../api/Client.ts"
|
|
||||||
import data from "../Data.ts"
|
|
||||||
import ChatFragment from "./chat/ChatFragment.jsx"
|
|
||||||
import LoginDialog from "./dialog/LoginDialog.tsx"
|
|
||||||
import ContactsListItem from "./main/ContactsListItem.jsx"
|
|
||||||
import RecentsListItem from "./main/RecentsListItem.jsx"
|
|
||||||
import snackbar from "./snackbar.js"
|
|
||||||
import useEventListener from './useEventListener.js'
|
|
||||||
|
|
||||||
import { MduiDialog, React, MduiNavigationRail, MduiTextField, MduiButton } from '../Imports.ts'
|
|
||||||
import User from "../api/client_data/User.ts"
|
|
||||||
import RecentChat from "../api/client_data/RecentChat.ts"
|
|
||||||
|
|
||||||
import '../typedef/mdui-jsx.d.ts'
|
|
||||||
|
|
||||||
declare function Split(r: unknown, s: unknown): {
|
|
||||||
setSizes?: undefined;
|
|
||||||
getSizes?: undefined;
|
|
||||||
collapse?: undefined;
|
|
||||||
destroy?: undefined;
|
|
||||||
parent?: undefined;
|
|
||||||
pairs?: undefined;
|
|
||||||
} | {
|
|
||||||
setSizes: (e: unknown) => void;
|
|
||||||
getSizes: () => unknown;
|
|
||||||
collapse: (e: unknown) => void;
|
|
||||||
destroy: (e: unknown, t: unknown) => void;
|
|
||||||
parent: unknown;
|
|
||||||
pairs: unknown[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function App() {
|
|
||||||
const [recentsList, setRecentsList] = React.useState([
|
|
||||||
{
|
|
||||||
id: '0',
|
|
||||||
avatar: "https://www.court-records.net/mugshot/aa6-004-maya.png",
|
|
||||||
title: "麻油衣酱",
|
|
||||||
content: "成步堂君, 我又坐牢了("
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '0',
|
|
||||||
avatar: "https://www.court-records.net/mugshot/aa6-004-maya.png",
|
|
||||||
title: "Maya Fey",
|
|
||||||
content: "我是绫里真宵, 是一名灵媒师~"
|
|
||||||
},
|
|
||||||
] as RecentChat[])
|
|
||||||
const [contactsMap, setContactsMap] = React.useState({
|
|
||||||
所有: [
|
|
||||||
{
|
|
||||||
id: '0',
|
|
||||||
avatar: "https://www.court-records.net/mugshot/aa6-004-maya.png",
|
|
||||||
nickname: "麻油衣酱",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '0',
|
|
||||||
avatar: "https://www.court-records.net/mugshot/aa6-004-maya.png",
|
|
||||||
nickname: "Maya Fey",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
} as unknown as { [key: string]: User[] })
|
|
||||||
const [navigationItemSelected, setNavigationItemSelected] = React.useState('Recents')
|
|
||||||
|
|
||||||
const navigationRailRef = React.useRef(null)
|
|
||||||
useEventListener(navigationRailRef, 'change', (event) => {
|
|
||||||
setNavigationItemSelected((event.target as HTMLElement as MduiNavigationRail).value as string)
|
|
||||||
})
|
|
||||||
|
|
||||||
const loginDialogRef: React.MutableRefObject<MduiDialog | null> = React.useRef(null)
|
|
||||||
const inputAccountRef: React.MutableRefObject<MduiTextField | null> = React.useRef(null)
|
|
||||||
const inputPasswordRef: React.MutableRefObject<MduiTextField | null> = React.useRef(null)
|
|
||||||
const registerButtonRef: React.MutableRefObject<MduiButton | null> = React.useRef(null)
|
|
||||||
const loginButtonRef: React.MutableRefObject<MduiButton | null> = React.useRef(null)
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
;(async () => {
|
|
||||||
Split(['#SideBar', '#ChatFragment'], {
|
|
||||||
sizes: [25, 75],
|
|
||||||
minSize: [200, 400],
|
|
||||||
gutterSize: 2,
|
|
||||||
})
|
|
||||||
|
|
||||||
Client.connect()
|
|
||||||
const re = await Client.invoke("User.auth", {
|
|
||||||
access_token: data.access_token,
|
|
||||||
})
|
|
||||||
if (re.code == 401)
|
|
||||||
loginDialogRef.current!.open = true
|
|
||||||
else if (re.code != 200)
|
|
||||||
snackbar("驗證失敗: " + re.msg)
|
|
||||||
})()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{
|
|
||||||
display: "flex",
|
|
||||||
position: 'relative',
|
|
||||||
width: 'calc(var(--whitesilk-window-width) - 80px)',
|
|
||||||
height: 'var(--whitesilk-window-height)',
|
|
||||||
}}>
|
|
||||||
<LoginDialog
|
|
||||||
loginDialogRef={loginDialogRef}
|
|
||||||
inputAccountRef={inputAccountRef}
|
|
||||||
inputPasswordRef={inputPasswordRef}
|
|
||||||
registerButtonRef={registerButtonRef}
|
|
||||||
loginButtonRef={loginButtonRef} />
|
|
||||||
<mdui-navigation-rail contained value="Recents" ref={navigationRailRef}>
|
|
||||||
<mdui-button-icon icon="menu" slot="top"></mdui-button-icon>
|
|
||||||
|
|
||||||
<mdui-navigation-rail-item icon="watch_later--outlined" value="Recents"></mdui-navigation-rail-item>
|
|
||||||
<mdui-navigation-rail-item icon="contacts--outlined" value="Contacts"></mdui-navigation-rail-item>
|
|
||||||
|
|
||||||
<mdui-button-icon icon="settings" slot="bottom"></mdui-button-icon>
|
|
||||||
</mdui-navigation-rail>
|
|
||||||
{
|
|
||||||
// 侧边列表
|
|
||||||
}
|
|
||||||
<div id="SideBar">
|
|
||||||
{
|
|
||||||
// 最近聊天
|
|
||||||
<mdui-list style={{
|
|
||||||
overflowY: 'auto',
|
|
||||||
paddingRight: '10px',
|
|
||||||
display: navigationItemSelected == "Recents" ? undefined : 'none'
|
|
||||||
}}>
|
|
||||||
{
|
|
||||||
recentsList.map((v) =>
|
|
||||||
<RecentsListItem
|
|
||||||
key={v.id}
|
|
||||||
nickName={v.title}
|
|
||||||
avatar={v.avatar}
|
|
||||||
content={v.content} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</mdui-list>
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// 联系人列表
|
|
||||||
<mdui-list style={{
|
|
||||||
overflowY: 'auto',
|
|
||||||
paddingRight: '10px',
|
|
||||||
display: navigationItemSelected == "Contacts" ? undefined : 'none'
|
|
||||||
}}>
|
|
||||||
<mdui-collapse accordion value={Object.keys(contactsMap)[0]}>
|
|
||||||
{
|
|
||||||
Object.keys(contactsMap).map((v) =>
|
|
||||||
<mdui-collapse-item key={v} value={v}>
|
|
||||||
<mdui-list-subheader slot="header">{v}</mdui-list-subheader>
|
|
||||||
{
|
|
||||||
contactsMap[v].map((v2) =>
|
|
||||||
<ContactsListItem
|
|
||||||
key={v2.id}
|
|
||||||
nickName={v2.nickname}
|
|
||||||
avatar={v2.avatar} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</mdui-collapse-item>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</mdui-collapse>
|
|
||||||
</mdui-list>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
{
|
|
||||||
// 聊天页面
|
|
||||||
}
|
|
||||||
<ChatFragment id="ChatFragment" />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import { React } from '../Imports.ts'
|
|
||||||
|
|
||||||
export default function Avatar({ src, text, icon = 'person', ...props } = {}) {
|
|
||||||
return (
|
|
||||||
src ? <mdui-avatar {...props}>
|
|
||||||
<img src={src} alt={'(头像)' + text || ''} />
|
|
||||||
</mdui-avatar>
|
|
||||||
: (
|
|
||||||
text ? <mdui-avatar {...props}>
|
|
||||||
{
|
|
||||||
text.substring(0, 0)
|
|
||||||
}
|
|
||||||
</mdui-avatar>
|
|
||||||
: <mdui-avatar icon={icon} {...props} />
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
25
client/ui/Avatar.tsx
Normal file
25
client/ui/Avatar.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
interface Args extends React.HTMLAttributes<HTMLElement> {
|
||||||
|
src?: string
|
||||||
|
text?: string
|
||||||
|
icon?: string
|
||||||
|
avatarRef?: React.LegacyRef<HTMLElement>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Avatar({
|
||||||
|
src,
|
||||||
|
text,
|
||||||
|
icon = 'person',
|
||||||
|
avatarRef,
|
||||||
|
...props
|
||||||
|
}: Args) {
|
||||||
|
if (src != null && src != '')
|
||||||
|
return <mdui-avatar ref={avatarRef} {...props} src={src} />
|
||||||
|
else if (text != null && text != '')
|
||||||
|
return <mdui-avatar ref={avatarRef} {...props}>
|
||||||
|
{
|
||||||
|
text.substring(0, 1)
|
||||||
|
}
|
||||||
|
</mdui-avatar>
|
||||||
|
else
|
||||||
|
return <mdui-avatar icon={icon} ref={avatarRef} {...props} />
|
||||||
|
}
|
||||||
34
client/ui/AvatarMySelf.tsx
Normal file
34
client/ui/AvatarMySelf.tsx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { UserMySelf } from "lingchair-client-protocol"
|
||||||
|
import useAsyncEffect from "../utils/useAsyncEffect.ts"
|
||||||
|
import Avatar from "./Avatar.tsx"
|
||||||
|
import getClient from "../getClient.ts"
|
||||||
|
import React from "react"
|
||||||
|
import sleep from "../utils/sleep.ts"
|
||||||
|
|
||||||
|
interface Args extends React.HTMLAttributes<HTMLElement> {
|
||||||
|
avatarRef?: React.LegacyRef<HTMLElement>
|
||||||
|
}
|
||||||
|
export default function AvatarMySelf({
|
||||||
|
avatarRef,
|
||||||
|
...props
|
||||||
|
}: Args) {
|
||||||
|
if (!avatarRef) avatarRef = React.useRef<HTMLElement>(null)
|
||||||
|
const [args, setArgs] = React.useState<{
|
||||||
|
text: string,
|
||||||
|
src: string,
|
||||||
|
}>({
|
||||||
|
text: '',
|
||||||
|
src: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
useAsyncEffect(async () => {
|
||||||
|
await sleep(200)
|
||||||
|
const mySelf = await UserMySelf.getMySelfOrThrow(getClient())
|
||||||
|
setArgs({
|
||||||
|
text: mySelf.getNickName(),
|
||||||
|
src: getClient().getUrlForFileByHash(mySelf.getAvatarFileHash(), '')!
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return <Avatar avatarRef={avatarRef} {...props} text={args.text} src={args.src}></Avatar>
|
||||||
|
}
|
||||||
19
client/ui/ImageViewer.tsx
Normal file
19
client/ui/ImageViewer.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { Dialog } from 'mdui'
|
||||||
|
import 'pinch-zoom-element'
|
||||||
|
import React from "react"
|
||||||
|
|
||||||
|
export default function ImageViewer() {
|
||||||
|
const dialogRef = React.useRef<Dialog>()
|
||||||
|
|
||||||
|
return <mdui-dialog ref={dialogRef} fullscreen="fullscreen">
|
||||||
|
<mdui-button-icon icon="open_in_new"
|
||||||
|
onclick="window.open(document.querySelector('#image-viewer-dialog-inner > *').src, '_blank')">
|
||||||
|
</mdui-button-icon>
|
||||||
|
<mdui-button-icon icon="close" onClick={() => dialogRef.current!.open = false}>
|
||||||
|
</mdui-button-icon>
|
||||||
|
{
|
||||||
|
// @ts-ignore 注册了这个元素
|
||||||
|
<pinch-zoom id="image-viewer-dialog-inner" style="width: var(--whitesilk-window-width); height: var(--whitesilk-window-height);"></pinch-zoom>
|
||||||
|
}
|
||||||
|
</mdui-dialog>
|
||||||
|
}
|
||||||
231
client/ui/Main.tsx
Normal file
231
client/ui/Main.tsx
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
import isMobileUI from "../utils/isMobileUI.ts"
|
||||||
|
import useEventListener from "../utils/useEventListener.ts"
|
||||||
|
import AvatarMySelf from "./AvatarMySelf.tsx"
|
||||||
|
import MainSharedContext from './MainSharedContext.ts'
|
||||||
|
import * as React from 'react'
|
||||||
|
import { BrowserRouter, Link, Outlet, Route, Routes } from "react-router"
|
||||||
|
import LoginDialog from "./main-page/LoginDialog.tsx"
|
||||||
|
import useAsyncEffect from "../utils/useAsyncEffect.ts"
|
||||||
|
import performAuth from "../performAuth.ts"
|
||||||
|
import { CallbackError, Chat, UserMySelf } from "lingchair-client-protocol"
|
||||||
|
import showCircleProgressDialog from "./showCircleProgressDialog.ts"
|
||||||
|
import RegisterDialog from "./main-page/RegisterDialog.tsx"
|
||||||
|
import sleep from "../utils/sleep.ts"
|
||||||
|
import { $, NavigationDrawer } from "mdui"
|
||||||
|
import getClient from "../getClient.ts"
|
||||||
|
import showSnackbar from "../utils/showSnackbar.ts"
|
||||||
|
import AllChatsList from "./main-page/AllChatsList.tsx"
|
||||||
|
import FavouriteChatsList from "./main-page/FavouriteChatsList.tsx"
|
||||||
|
import AddFavourtieChatDialog from "./main-page/AddFavourtieChatDialog.tsx"
|
||||||
|
import RecentChatsList from "./main-page/RecentChatsList.tsx"
|
||||||
|
import ChatInfoDialog from "./routers/ChatInfoDialog.tsx"
|
||||||
|
|
||||||
|
export default function Main() {
|
||||||
|
const [myProfileCache, setMyProfileCache] = React.useState<UserMySelf>()
|
||||||
|
|
||||||
|
// 多页面切换
|
||||||
|
const navigationRef = React.useRef<HTMLElement>()
|
||||||
|
const [currentShowPage, setCurrentShowPage] = React.useState('Recents')
|
||||||
|
type HTMLElementWithValue = HTMLElement & { value: string }
|
||||||
|
useEventListener(navigationRef, 'change', (event) => {
|
||||||
|
setCurrentShowPage((event.target as HTMLElementWithValue).value)
|
||||||
|
})
|
||||||
|
|
||||||
|
const drawerRef = React.useRef<NavigationDrawer>()
|
||||||
|
React.useEffect(() => {
|
||||||
|
$(drawerRef.current!.shadowRoot).append(`
|
||||||
|
<style>
|
||||||
|
.panel {
|
||||||
|
width: 17.5rem !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const [showLoginDialog, setShowLoginDialog] = React.useState(false)
|
||||||
|
const [showRegisterDialog, setShowRegisterDialog] = React.useState(false)
|
||||||
|
const [showAddFavourtieChatDialog, setShowAddFavourtieChatDialog] = React.useState(false)
|
||||||
|
|
||||||
|
const [currentSelectedChatId, setCurrentSelectedChatId] = React.useState('')
|
||||||
|
|
||||||
|
const [favouriteChats, setFavouriteChats] = React.useState<Chat[]>([])
|
||||||
|
|
||||||
|
const sharedContext = {
|
||||||
|
functions_lazy: React.useRef({
|
||||||
|
updateFavouriteChats: () => { },
|
||||||
|
updateRecentChats: () => { },
|
||||||
|
updateAllChats: () => { },
|
||||||
|
}),
|
||||||
|
favouriteChats,
|
||||||
|
setFavouriteChats,
|
||||||
|
|
||||||
|
setShowLoginDialog,
|
||||||
|
setShowRegisterDialog,
|
||||||
|
setShowAddFavourtieChatDialog,
|
||||||
|
|
||||||
|
currentSelectedChatId,
|
||||||
|
setCurrentSelectedChatId,
|
||||||
|
|
||||||
|
myProfileCache,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
useAsyncEffect(async () => {
|
||||||
|
const waitingForAuth = showCircleProgressDialog("加载中...")
|
||||||
|
try {
|
||||||
|
await performAuth({})
|
||||||
|
|
||||||
|
try {
|
||||||
|
setMyProfileCache(await UserMySelf.getMySelfOrThrow(getClient()))
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof CallbackError)
|
||||||
|
showSnackbar({
|
||||||
|
message: '获取资料失败: ' + e.message
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof CallbackError)
|
||||||
|
if (e.code == 401 || e.code == 400)
|
||||||
|
setShowLoginDialog(true)
|
||||||
|
}
|
||||||
|
// 动画都没来得及, 稍微等一下 (
|
||||||
|
await sleep(100)
|
||||||
|
waitingForAuth.open = false
|
||||||
|
})
|
||||||
|
|
||||||
|
const subRoutes = <>
|
||||||
|
<Route path="/info">
|
||||||
|
<Route path="chat" element={<ChatInfoDialog />} />
|
||||||
|
<Route path="user" element={<ChatInfoDialog />} />
|
||||||
|
</Route>
|
||||||
|
</>
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MainSharedContext.Provider value={sharedContext}>
|
||||||
|
<BrowserRouter>
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={(
|
||||||
|
<div style={{
|
||||||
|
display: "flex",
|
||||||
|
position: 'relative',
|
||||||
|
flexDirection: isMobileUI() ? 'column' : 'row',
|
||||||
|
width: `calc(var(--whitesilk-window-width))${isMobileUI() ? '' : ' - 80px'}`,
|
||||||
|
height: 'var(--whitesilk-window-height)',
|
||||||
|
}}>
|
||||||
|
{
|
||||||
|
// 将子路由渲染到此处
|
||||||
|
<Outlet />
|
||||||
|
}
|
||||||
|
<LoginDialog open={showLoginDialog} />
|
||||||
|
<RegisterDialog open={showRegisterDialog} />
|
||||||
|
<AddFavourtieChatDialog open={showAddFavourtieChatDialog} />
|
||||||
|
<mdui-navigation-drawer ref={drawerRef} modal close-on-esc close-on-overlay-click>
|
||||||
|
<mdui-list style={{
|
||||||
|
padding: '10px',
|
||||||
|
}}>
|
||||||
|
<mdui-list-item rounded>
|
||||||
|
<span>{myProfileCache?.getNickName()}</span>
|
||||||
|
<AvatarMySelf slot="icon" />
|
||||||
|
</mdui-list-item>
|
||||||
|
<mdui-list-item rounded icon="manage_accounts">账号设置</mdui-list-item>
|
||||||
|
<mdui-divider style={{
|
||||||
|
margin: '10px',
|
||||||
|
}}></mdui-divider>
|
||||||
|
<mdui-list-item rounded icon="person_add">添加收藏对话</mdui-list-item>
|
||||||
|
<mdui-list-item rounded icon="group_add">创建新的群组</mdui-list-item>
|
||||||
|
<Link to="/info/user?id=0960bd15-4527-4000-97a8-73110160296f"><mdui-list-item rounded icon="group_add">我是测试</mdui-list-item></Link>
|
||||||
|
<Link to="/info/chat?id=priv_0960bd15_4527_4000_97a8_73110160296f__0960bd15_4527_4000_97a8_73110160296f"><mdui-list-item rounded icon="group_add">我是测试2</mdui-list-item></Link>
|
||||||
|
</mdui-list>
|
||||||
|
<div style={{
|
||||||
|
flexGrow: 1,
|
||||||
|
}}></div>
|
||||||
|
<span style={{
|
||||||
|
padding: '10px',
|
||||||
|
fontSize: 'small',
|
||||||
|
}}>
|
||||||
|
LingChair Web v{__APP_VERSION__}<br />
|
||||||
|
Build: <a href={`https://codeberg.org/CrescentLeaf/LingChair/src/commit/${__GIT_HASH_FULL__}`}>{__GIT_HASH__}</a> ({__BUILD_TIME__})<br />
|
||||||
|
在 Codeberg 上<a href="https://codeberg.org/CrescentLeaf/LingChair">查看源代码</a>
|
||||||
|
</span>
|
||||||
|
</mdui-navigation-drawer>
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Default: 侧边列表提供列表切换
|
||||||
|
*/
|
||||||
|
!isMobileUI() ?
|
||||||
|
<mdui-navigation-rail ref={navigationRef} contained value="Recents">
|
||||||
|
<mdui-button-icon slot="top" icon="menu" onClick={() => drawerRef.current!.open = true}></mdui-button-icon>
|
||||||
|
|
||||||
|
<mdui-navigation-rail-item icon="watch_later--outlined" active-icon="watch_later--filled" value="Recents"></mdui-navigation-rail-item>
|
||||||
|
<mdui-navigation-rail-item icon="favorite_border" active-icon="favorite" value="Favourites"></mdui-navigation-rail-item>
|
||||||
|
<mdui-navigation-rail-item icon="chat--outlined" active-icon="chat--filled" value="AllChats"></mdui-navigation-rail-item>
|
||||||
|
</mdui-navigation-rail>
|
||||||
|
/**
|
||||||
|
* Mobile: 底部导航栏提供列表切换
|
||||||
|
*/
|
||||||
|
: <mdui-top-app-bar style={{
|
||||||
|
position: 'sticky',
|
||||||
|
marginTop: '3px',
|
||||||
|
marginRight: '6px',
|
||||||
|
marginLeft: '15px',
|
||||||
|
top: '0px',
|
||||||
|
}}>
|
||||||
|
<mdui-button-icon icon="menu" onClick={() => drawerRef.current!.open = true}></mdui-button-icon>
|
||||||
|
<mdui-top-app-bar-title>{
|
||||||
|
({
|
||||||
|
Recents: "最近对话",
|
||||||
|
Favourites: "收藏对话",
|
||||||
|
AllChats: "所有对话",
|
||||||
|
})[currentShowPage]
|
||||||
|
}</mdui-top-app-bar-title>
|
||||||
|
<div style={{
|
||||||
|
flexGrow: 1,
|
||||||
|
}}></div>
|
||||||
|
</mdui-top-app-bar>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Mobile: 指定高度的容器
|
||||||
|
* Default: 侧边列表
|
||||||
|
*/
|
||||||
|
<div style={isMobileUI() ? {
|
||||||
|
display: 'flex',
|
||||||
|
height: 'calc(100% - 80px - 67px)',
|
||||||
|
width: '100%',
|
||||||
|
} : {}} id="SideBar">
|
||||||
|
<RecentChatsList style={{
|
||||||
|
display: currentShowPage == 'Recents' ? undefined : 'none'
|
||||||
|
}} />
|
||||||
|
<FavouriteChatsList style={{
|
||||||
|
display: currentShowPage == 'Favourites' ? undefined : 'none'
|
||||||
|
}} />
|
||||||
|
<AllChatsList style={{
|
||||||
|
display: currentShowPage == 'AllChats' ? undefined : 'none'
|
||||||
|
}} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Mobile: 底部导航栏提供列表切换
|
||||||
|
* Default: 侧边列表提供列表切换
|
||||||
|
*/
|
||||||
|
isMobileUI() && <mdui-navigation-bar ref={navigationRef} label-visibility="selected" value="Recents" style={{
|
||||||
|
position: 'sticky',
|
||||||
|
bottom: '0',
|
||||||
|
}}>
|
||||||
|
<mdui-navigation-bar-item icon="watch_later--outlined" active-icon="watch_later--filled" value="Recents">最近对话</mdui-navigation-bar-item>
|
||||||
|
<mdui-navigation-bar-item icon="favorite_border" active-icon="favorite" value="Favourites">收藏对话</mdui-navigation-bar-item>
|
||||||
|
<mdui-navigation-bar-item icon="chat--outlined" active-icon="chat--filled" value="AllChats">全部对话</mdui-navigation-bar-item>
|
||||||
|
</mdui-navigation-bar>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)}>
|
||||||
|
{subRoutes}
|
||||||
|
</Route>
|
||||||
|
</Routes>
|
||||||
|
</BrowserRouter>
|
||||||
|
</MainSharedContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
23
client/ui/MainSharedContext.ts
Normal file
23
client/ui/MainSharedContext.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { Chat, UserMySelf } from "lingchair-client-protocol"
|
||||||
|
import { createContext } from "use-context-selector"
|
||||||
|
|
||||||
|
type Shared = {
|
||||||
|
functions_lazy: React.MutableRefObject<{
|
||||||
|
updateFavouriteChats: () => void
|
||||||
|
updateRecentChats: () => void
|
||||||
|
updateAllChats: () => void
|
||||||
|
}>
|
||||||
|
favouriteChats: Chat[]
|
||||||
|
setFavouriteChats: React.Dispatch<React.SetStateAction<Chat[]>>
|
||||||
|
setShowLoginDialog: React.Dispatch<React.SetStateAction<boolean>>
|
||||||
|
setShowRegisterDialog: React.Dispatch<React.SetStateAction<boolean>>
|
||||||
|
setShowAddFavourtieChatDialog: React.Dispatch<React.SetStateAction<boolean>>
|
||||||
|
setCurrentSelectedChatId: React.Dispatch<React.SetStateAction<string>>
|
||||||
|
myProfileCache?: UserMySelf
|
||||||
|
currentSelectedChatId: string
|
||||||
|
}
|
||||||
|
const MainSharedContext = createContext({} as Shared)
|
||||||
|
|
||||||
|
export default MainSharedContext
|
||||||
|
|
||||||
|
export type { Shared }
|
||||||
39
client/ui/chat-elements/chat-file.ts
Normal file
39
client/ui/chat-elements/chat-file.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { $ } from 'mdui/jq'
|
||||||
|
|
||||||
|
customElements.define('chat-file', class extends HTMLElement {
|
||||||
|
static observedAttributes = ['href', 'name']
|
||||||
|
declare anchor: HTMLAnchorElement
|
||||||
|
declare span: HTMLSpanElement
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
this.attachShadow({ mode: 'open' })
|
||||||
|
}
|
||||||
|
update() {
|
||||||
|
if (this.anchor == null) return
|
||||||
|
|
||||||
|
this.anchor.href = $(this).attr('href') as string
|
||||||
|
this.anchor.download = $(this).attr('href') as string
|
||||||
|
this.span.textContent = $(this).attr("name") as string
|
||||||
|
}
|
||||||
|
attributeChangedCallback(_name: string, _oldValue: unknown, _newValue: unknown) {
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
connectedCallback() {
|
||||||
|
this.anchor = new DOMParser().parseFromString(`
|
||||||
|
<a style="width: 100%;height: 100%;">
|
||||||
|
<mdui-card clickable style="display: flex;align-items: center;box-shadow: inherit;border-radius: inherit;">
|
||||||
|
<mdui-icon name="insert_drive_file" style="margin: 13px;font-size: 34px;"></mdui-icon>
|
||||||
|
<span style="margin-right: 13px; word-wrap: break-word; word-break:break-all;white-space:normal; max-width :100%;"></span>
|
||||||
|
</mdui-card>
|
||||||
|
</a>`, 'text/html').body.firstChild as HTMLAnchorElement
|
||||||
|
this.span = $(this.anchor).find('span').get(0)
|
||||||
|
this.anchor.style.textDecoration = 'none'
|
||||||
|
this.anchor.style.color = 'inherit'
|
||||||
|
this.anchor.onclick = (e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
this.shadowRoot!.appendChild(this.anchor)
|
||||||
|
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
})
|
||||||
58
client/ui/chat-elements/chat-image.ts
Normal file
58
client/ui/chat-elements/chat-image.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import openImageViewer from "../../utils/openImageViewer.ts"
|
||||||
|
|
||||||
|
import { $ } from 'mdui/jq'
|
||||||
|
|
||||||
|
|
||||||
|
customElements.define('chat-image', class extends HTMLElement {
|
||||||
|
static observedAttributes = ['src', 'show-error']
|
||||||
|
declare img: HTMLImageElement
|
||||||
|
declare error: HTMLElement
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.attachShadow({ mode: 'open' })
|
||||||
|
}
|
||||||
|
update() {
|
||||||
|
if (this.img == null) return
|
||||||
|
|
||||||
|
this.img.src = $(this).attr('src') as string
|
||||||
|
|
||||||
|
const error = $(this).attr('show-error') == 'true'
|
||||||
|
this.img.style.display = error ? 'none' : 'block'
|
||||||
|
this.error.style.display = error ? '' : 'none'
|
||||||
|
}
|
||||||
|
attributeChangedCallback(_name: string, _oldValue: unknown, _newValue: unknown) {
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
connectedCallback() {
|
||||||
|
this.img = new Image()
|
||||||
|
this.img.style.width = '100%'
|
||||||
|
this.img.style.maxHeight = "300px"
|
||||||
|
this.img.style.objectFit = 'cover'
|
||||||
|
// this.img.style.borderRadius = "var(--mdui-shape-corner-medium)"
|
||||||
|
this.shadowRoot!.appendChild(this.img)
|
||||||
|
|
||||||
|
this.error = new DOMParser().parseFromString(`<mdui-icon name="broken_image" style="font-size: 2rem;"></mdui-icon>`, 'text/html').body.firstChild as HTMLElement
|
||||||
|
this.shadowRoot!.appendChild(this.error)
|
||||||
|
|
||||||
|
this.img.addEventListener('error', () => {
|
||||||
|
$(this).attr('show-error', 'true')
|
||||||
|
})
|
||||||
|
this.error.addEventListener('click', (event) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
const img = this.img
|
||||||
|
this.img = new Image()
|
||||||
|
this.img.style.width = '100%'
|
||||||
|
this.img.style.maxHeight = "300px"
|
||||||
|
this.img.style.objectFit = 'cover'
|
||||||
|
this.shadowRoot!.replaceChild(img, this.img)
|
||||||
|
$(this).attr('show-error', undefined)
|
||||||
|
})
|
||||||
|
this.img.addEventListener('click', (event) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
openImageViewer($(this).attr('src') as string)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
})
|
||||||
60
client/ui/chat-elements/chat-mention.ts
Normal file
60
client/ui/chat-elements/chat-mention.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { $ } from 'mdui'
|
||||||
|
import showSnackbar from "../../utils/showSnackbar.ts";
|
||||||
|
customElements.define('chat-mention', class extends HTMLElement {
|
||||||
|
declare link: HTMLAnchorElement
|
||||||
|
static observedAttributes = ['user-id']
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.attachShadow({ mode: 'open' })
|
||||||
|
}
|
||||||
|
connectedCallback() {
|
||||||
|
const shadow = this.shadowRoot as ShadowRoot
|
||||||
|
|
||||||
|
this.link = document.createElement('a')
|
||||||
|
this.link.style.fontSynthesis = 'style weight'
|
||||||
|
this.link.style.color = 'rgb(var(--mdui-color-primary))'
|
||||||
|
this.link.href = 'javascript:void(0)'
|
||||||
|
shadow.appendChild(this.link)
|
||||||
|
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
attributeChangedCallback(_name: string, _oldValue: unknown, _newValue: unknown) {
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
async update() {
|
||||||
|
if (this.link == null) return
|
||||||
|
|
||||||
|
const userId = $(this).attr('user-id')
|
||||||
|
const chatId = $(this).attr('chat-id')
|
||||||
|
const text = $(this).attr('text')
|
||||||
|
this.link.style.fontStyle = ''
|
||||||
|
if (chatId) {
|
||||||
|
|
||||||
|
this.link.onclick = (e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
// deno-lint-ignore no-window
|
||||||
|
|
||||||
|
}
|
||||||
|
} else if (userId) {
|
||||||
|
|
||||||
|
this.link.onclick = (e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
// deno-lint-ignore no-window
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text && (this.link.textContent = text)
|
||||||
|
if (!(userId || chatId)) {
|
||||||
|
this.link.textContent = "无效的提及"
|
||||||
|
this.link.style.fontStyle = 'italic'
|
||||||
|
this.link.onclick = (e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
showSnackbar({
|
||||||
|
message: "该提及没有指定用户或者对话!",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
45
client/ui/chat-elements/chat-quote.ts
Normal file
45
client/ui/chat-elements/chat-quote.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { $ } from 'mdui/jq'
|
||||||
|
|
||||||
|
customElements.define('chat-quote', class extends HTMLElement {
|
||||||
|
declare container: HTMLAnchorElement
|
||||||
|
declare span: HTMLSpanElement
|
||||||
|
declare ellipsis: boolean
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
this.attachShadow({ mode: 'open' })
|
||||||
|
}
|
||||||
|
update() {
|
||||||
|
if (this.container == null) return
|
||||||
|
|
||||||
|
this.span.textContent = this.textContent
|
||||||
|
|
||||||
|
this.updateStyle()
|
||||||
|
}
|
||||||
|
updateStyle() {
|
||||||
|
this.span.style.whiteSpace = this.ellipsis ? 'nowrap' : 'pre-wrap'
|
||||||
|
this.span.style.overflow = this.ellipsis ? 'hidden' : ''
|
||||||
|
this.span.style.textOverflow = this.ellipsis ? 'ellipsis' : ''
|
||||||
|
}
|
||||||
|
attributeChangedCallback(_name: string, _oldValue: unknown, _newValue: unknown) {
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
connectedCallback() {
|
||||||
|
this.container = new DOMParser().parseFromString(`
|
||||||
|
<a style="width: 100%;height: 100%; color: rgb(var(--mdui-color-primary));" href="javascript:void(0)">
|
||||||
|
<span style="display: block; word-wrap: break-word; word-break:break-all;white-space:normal; max-width :100%;"></span>
|
||||||
|
</a>`, 'text/html').body.firstChild as HTMLAnchorElement
|
||||||
|
this.span = $(this.container).find('span').get(0)
|
||||||
|
this.container.style.textDecoration = 'none'
|
||||||
|
this.span.style.fontSynthesis = 'style weight'
|
||||||
|
this.container.onclick = (e) => {
|
||||||
|
this.ellipsis = !this.ellipsis
|
||||||
|
this.updateStyle()
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
this.ellipsis = true
|
||||||
|
|
||||||
|
this.shadowRoot!.appendChild(this.container)
|
||||||
|
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
})
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user