forked from naskya/firefish
feat: accept more signature algorithms
enhances 8890902675
Co-authored-by: naskya <m@naskya.net>
This commit is contained in:
parent
1ccf256b99
commit
85d5e7bd3b
3 changed files with 74 additions and 23 deletions
|
@ -40,6 +40,7 @@
|
|||
|
||||
## 細かい変更点
|
||||
|
||||
- 署名アルゴリズムとして ECDSA や Ed25519 なども受け入れる([github.com/mei23/misskey-v12](https://github.com/mei23/misskey-v12) から取り込み)
|
||||
- Pleroma のチャットに対応(Catodon から取り込み)
|
||||
- 翻訳機能にて、投稿言語が指定されていない場合にのみ言語の自動検出を用いるように変更
|
||||
- アップデート時に更新内容を確認できる機能を追加
|
||||
|
|
|
@ -10,8 +10,6 @@ import type { IncomingMessage } from "http";
|
|||
import type { CacheableRemoteUser } from "@/models/entities/user.js";
|
||||
import type { UserPublickey } from "@/models/entities/user-publickey.js";
|
||||
import { verify } from "node:crypto";
|
||||
import { toSingle } from "@/prelude/array.js";
|
||||
import { createHash } from "node:crypto";
|
||||
|
||||
export async function hasSignature(req: IncomingMessage): Promise<string> {
|
||||
const meta = await fetchMeta();
|
||||
|
@ -158,20 +156,3 @@ export function verifySignature(
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function verifyDigest(
|
||||
body: string,
|
||||
digest: string | string[] | undefined,
|
||||
): boolean {
|
||||
digest = toSingle(digest);
|
||||
if (
|
||||
body == null ||
|
||||
digest == null ||
|
||||
!digest.toLowerCase().startsWith("sha-256=")
|
||||
)
|
||||
return false;
|
||||
|
||||
return (
|
||||
createHash("sha256").update(body).digest("base64") === digest.substring(8)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import { getUserKeypair } from "@/misc/keypair-store.js";
|
|||
import {
|
||||
checkFetch,
|
||||
getSignatureUser,
|
||||
verifyDigest,
|
||||
} from "@/remote/activitypub/check-fetch.js";
|
||||
import { getInstanceActor } from "@/services/instance-actor.js";
|
||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
||||
|
@ -35,6 +34,9 @@ import Outbox, { packActivity } from "./activitypub/outbox.js";
|
|||
import { serverLogger } from "./index.js";
|
||||
import config from "@/config/index.js";
|
||||
import Koa from "koa";
|
||||
import * as crypto from "node:crypto";
|
||||
import { inspect } from "node:util";
|
||||
import type { IActivity } from "@/remote/activitypub/type.js";
|
||||
|
||||
// Init router
|
||||
const router = new Router();
|
||||
|
@ -43,27 +45,94 @@ const router = new Router();
|
|||
|
||||
function inbox(ctx: Router.RouterContext) {
|
||||
if (ctx.req.headers.host !== config.host) {
|
||||
serverLogger.warn("inbox: Invalid Host");
|
||||
ctx.status = 400;
|
||||
ctx.message = "Invalid Host";
|
||||
return;
|
||||
}
|
||||
|
||||
let signature;
|
||||
let signature: httpSignature.IParsedSignature;
|
||||
|
||||
try {
|
||||
signature = httpSignature.parseRequest(ctx.req, {
|
||||
headers: ["(request-target)", "digest", "host", "date"],
|
||||
});
|
||||
} catch (e) {
|
||||
serverLogger.warn(`inbox: signature parse error: ${inspect(e)}`);
|
||||
ctx.status = 401;
|
||||
|
||||
if (e instanceof Error) {
|
||||
if (e.name === "ExpiredRequestError")
|
||||
ctx.message = "Expired Request Error";
|
||||
if (e.name === "MissingHeaderError")
|
||||
ctx.message = "Missing Required Header";
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!verifyDigest(ctx.request.rawBody, ctx.headers.digest)) {
|
||||
// Validate signature algorithm
|
||||
if (
|
||||
!signature.algorithm
|
||||
.toLowerCase()
|
||||
.match(/^((dsa|rsa|ecdsa)-(sha256|sha384|sha512)|ed25519-sha512|hs2019)$/)
|
||||
) {
|
||||
serverLogger.warn(
|
||||
`inbox: invalid signature algorithm ${signature.algorithm}`,
|
||||
);
|
||||
ctx.status = 401;
|
||||
ctx.message = "Invalid Signature Algorithm";
|
||||
return;
|
||||
|
||||
// hs2019
|
||||
// keyType=ED25519 => ed25519-sha512
|
||||
// keyType=other => (keyType)-sha256
|
||||
}
|
||||
|
||||
// Validate digest header
|
||||
const digest = ctx.req.headers.digest;
|
||||
|
||||
if (typeof digest !== "string") {
|
||||
serverLogger.warn(
|
||||
"inbox: zero or more than one digest header(s) are present",
|
||||
);
|
||||
ctx.status = 401;
|
||||
ctx.message = "Invalid Digest Header";
|
||||
return;
|
||||
}
|
||||
|
||||
processInbox(ctx.request.body, signature);
|
||||
const match = digest.match(/^([0-9A-Za-z-]+)=(.+)$/);
|
||||
|
||||
if (match == null) {
|
||||
serverLogger.warn("inbox: unrecognized digest header");
|
||||
ctx.status = 401;
|
||||
ctx.message = "Invalid Digest Header";
|
||||
return;
|
||||
}
|
||||
|
||||
const digestAlgo = match[1];
|
||||
const expectedDigest = match[2];
|
||||
|
||||
if (digestAlgo.toUpperCase() !== "SHA-256") {
|
||||
serverLogger.warn("inbox: unsupported digest algorithm");
|
||||
ctx.status = 401;
|
||||
ctx.message = "Unsupported Digest Algorithm";
|
||||
return;
|
||||
}
|
||||
|
||||
const actualDigest = crypto
|
||||
.createHash("sha256")
|
||||
.update(ctx.request.rawBody)
|
||||
.digest("base64");
|
||||
|
||||
if (expectedDigest !== actualDigest) {
|
||||
serverLogger.warn("inbox: Digest Mismatch");
|
||||
ctx.status = 401;
|
||||
ctx.message = "Digest Missmatch";
|
||||
return;
|
||||
}
|
||||
|
||||
processInbox(ctx.request.body as IActivity, signature);
|
||||
|
||||
ctx.status = 202;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue