Skip to content

Commit

Permalink
feat: support new API auth method
Browse files Browse the repository at this point in the history
  • Loading branch information
DreamOfIce committed Jul 17, 2023
1 parent bc4836a commit 584026d
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 30 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
- [ ] Oauth 登录
- [ ] 权限系统

> 以下功能需要等待官方的 API 支持:
> 以下功能需要等待官方的 API 支持:
- [ ] 用户退群事件(`guild-member-deleted`)
- [ ] 富文本(粗体、斜体、下划线等)
- [ ] 私信
- [ ] 用户退群事件(`guild-member-deleted`)
- [ ] 富文本(粗体、斜体、下划线等)
- [ ] 私信

## 配置

Expand Down
43 changes: 17 additions & 26 deletions src/bot.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
base64ToArrayBuffer,
Bot,
type Context,
type Fragment,
Expand All @@ -10,6 +9,7 @@ import {
} from "koishi";
import { VillaBotConfig } from "./config";
import {
calcSecretHash,
createAxios,
defineStruct,
deleteMessage,
Expand All @@ -23,11 +23,11 @@ import {
registerCallbackRoute,
removeCallbackRoute,
transferImage,
verifyCallback,
} from "./utils";
import type { KoaContext } from "./types";
import { Callback, Message } from "./structs";
import { VillaMessanger } from "./messanger";
import { webcrypto } from "crypto";

export class VillaBot extends Bot<VillaBotConfig> {
/** bot id */
Expand All @@ -50,7 +50,7 @@ export class VillaBot extends Bot<VillaBotConfig> {
this.secret = config.secret;
this.selfId = config.id;

this.axios = createAxios(ctx, config.id, config.secret, this.apiServer);
this.axios = ctx.http;
}

public onError(error: Error) {
Expand All @@ -59,6 +59,14 @@ export class VillaBot extends Bot<VillaBotConfig> {

public override async start(): Promise<void> {
await super.start();
this.axios = createAxios(
this.ctx,
this.config.id,
this.config.pubKey
? await calcSecretHash(this.config.secret, this.config.pubKey)
: this.config.secret,
this.apiServer
);
registerCallbackRoute(
this.config.path,
this.ctx,
Expand Down Expand Up @@ -95,31 +103,14 @@ export class VillaBot extends Bot<VillaBotConfig> {
ctx.status = 400;
return;
}
// TODO: remove this because `pubKey` will be a required config since next version
if (this.config.pubKey) {
const sign = base64ToArrayBuffer(ctx.header["x-rpc-bot_sign"] as string);
const data = new URLSearchParams({
body: ctx.request.rawBody!,
secret: this.config.secret,
}).toString();
const hash = await webcrypto.subtle.digest(
"SHA-256",
new TextEncoder().encode(data)
);

const publicKey = await webcrypto.subtle.importKey(
"spki",
base64ToArrayBuffer(this.config.pubKey.slice(26, -24).trim()),
{ name: "RSA-PKCS1-v1_5", hash: "SHA-256" },
false,
["verify"]
);
if (this.config.pubKey) {
if (
!(await webcrypto.subtle.verify(
"RSASSA-PKCS1-v1_5",
publicKey,
sign,
hash
!(await verifyCallback(
ctx.header["x-rpc-bot_sign"] as string,
this.config.secret,
this.config.pubKey,
ctx.request.rawBody
))
) {
ctx.body = defineStruct<Callback.Response>({
Expand Down
1 change: 1 addition & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export interface VillaBotConfig extends Bot.Config {
id: string;
secret: string;
path: string;
// TODO: mark `pubKey` as required because old API auth will be removed since Aug. 8th, 2023
pubKey?: string;
transfer: {
maxRetries: number;
Expand Down
3 changes: 3 additions & 0 deletions src/messanger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ export class VillaMessanger extends Messenger<VillaBot> {
case "image": {
const url = (element.attrs as Dict<string, "url">)["url"];
const newUrl = await this.bot.transferImage(url);
const { protocol } = new URL(newUrl);
if (protocol !== "http:" && protocol !== "https:")
throw new Error(`Unsupported image protocol: ${protocol}`);
await this.flush();
const msg: Message.MsgContentInfo<Message.ImageMsgContent> = {
content: {
Expand Down
15 changes: 15 additions & 0 deletions src/utils/calcSecretHash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { webcrypto } from "crypto";
import { importPublicKey } from "./importPublicKey";

export const calcSecretHash = async (secret: string, pubKey: string) => {
const publicKey = await importPublicKey(pubKey, {
name: "HMAC",
hash: "SHA256",
});
const hash = await webcrypto.subtle.sign(
"HMAC",
publicKey,
new TextEncoder().encode(secret)
);
return new TextDecoder().decode(hash);
};
14 changes: 14 additions & 0 deletions src/utils/importPublicKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { webcrypto } from "crypto";
import { base64ToArrayBuffer } from "koishi";

export const importPublicKey = async (
pemKey: string,
algorithm: Parameters<webcrypto.SubtleCrypto["importKey"]>[2]
) =>
await webcrypto.subtle.importKey(
"spki",
base64ToArrayBuffer(pemKey.slice(26, -24).trim()),
algorithm,
false,
["sign", "verify"]
);
3 changes: 3 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./calcSecretHash";
export * from "./callbackRoute";
export * from "./createAxios";
export * from "./defineStruct";
Expand All @@ -7,7 +8,9 @@ export * from "./getChannelList";
export * from "./getGuild";
export * from "./getGuildMemberList";
export * from "./getUser";
export * from "./importPublicKey";
export * from "./isBot";
export * from "./logger";
export * from "./parseMessage";
export * from "./transferImage";
export * from "./verifyCallback";
31 changes: 31 additions & 0 deletions src/utils/verifyCallback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { webcrypto } from "crypto";
import { base64ToArrayBuffer } from "koishi";
import { importPublicKey } from "./importPublicKey";

export const verifyCallback = async (
signature: string,
secret: string,
pubKey: string,
body?: string
) => {
const sign = base64ToArrayBuffer(signature);
const data = new URLSearchParams({
body: body ?? "",
secret,
}).toString();
const hash = await webcrypto.subtle.digest(
"SHA-256",
new TextEncoder().encode(data)
);

const publicKey = await importPublicKey(pubKey, {
name: "RSA-PKCS1-v1_5",
hash: "SHA-256",
});
return await webcrypto.subtle.verify(
"RSASSA-PKCS1-v1_5",
publicKey,
sign,
hash
);
};

0 comments on commit 584026d

Please sign in to comment.