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_data/
|
||||
|
||||
deno.lock
|
||||
# Node.js
|
||||
package-lock.json
|
||||
node_modules/
|
||||
|
||||
8
.vscode/launch.json
vendored
8
.vscode/launch.json
vendored
@@ -5,10 +5,10 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "deno task debug",
|
||||
"name": "Run deno task debug",
|
||||
"command": "npm run debug",
|
||||
"name": "Debug",
|
||||
"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" />
|
||||
|
||||
<!-- UI -->
|
||||
<script src="./static/mdui-2.1.4.global.min.js">
|
||||
</script>
|
||||
<link rel="icon" href="icon.ico" />
|
||||
<link rel="stylesheet" href="./static/mdui-2.1.4.css" />
|
||||
<link rel="stylesheet" href="./static/material_icons.css" />
|
||||
|
||||
<script src="./static/jquery-3.7.1.min.js"></script>
|
||||
<script src="./static/splitjs-1.6.5.min.js"></script>
|
||||
<title>LingChair</title>
|
||||
|
||||
<title>TheWhiteSilk</title>
|
||||
|
||||
<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>
|
||||
<link rel="stylesheet" href="./style.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<mdui-snackbar close-on-outside-click id="public_snackbar"></mdui-snackbar>
|
||||
|
||||
<mdui-dialog close-on-overlay-click id="ErrorDialog">
|
||||
<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>
|
||||
<script type="module" src="./init.ts"></script>
|
||||
</body>
|
||||
|
||||
</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