Fixed 267U.pre2
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
import { URL } from "url";
|
||||
import httpSignature from "@peertube/http-signature";
|
||||
import config from "../../config/index.js";
|
||||
import { fetchMeta } from "../../misc/fetch-meta.js";
|
||||
import { toPuny } from "../../misc/convert-host.js";
|
||||
import DbResolver from "./db-resolver.js";
|
||||
import { getApId } from "./type.js";
|
||||
import { shouldBlockInstance } from "../../misc/should-block-instance.js";
|
||||
import { verify } from "node:crypto";
|
||||
import { toSingle } from "../../prelude/array.js";
|
||||
import { createHash } from "node:crypto";
|
||||
import { tickFetch } from "../../metrics.js";
|
||||
export async function hasSignature(req) {
|
||||
const meta = await fetchMeta();
|
||||
const required = meta.secureMode || meta.privateMode;
|
||||
try {
|
||||
httpSignature.parseRequest(req, {
|
||||
headers: []
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error && e.name === "MissingHeaderError") {
|
||||
return required ? "missing" : "optional";
|
||||
}
|
||||
return "invalid";
|
||||
}
|
||||
return required ? "supplied" : "unneeded";
|
||||
}
|
||||
export async function checkFetch(req) {
|
||||
const meta = await fetchMeta();
|
||||
if (meta.secureMode || meta.privateMode) {
|
||||
if (req.headers.host !== config.host) return 400;
|
||||
let signature;
|
||||
try {
|
||||
signature = httpSignature.parseRequest(req, {
|
||||
headers: [
|
||||
"(request-target)",
|
||||
"host",
|
||||
"date"
|
||||
],
|
||||
authorizationHeaderName: 'signature'
|
||||
});
|
||||
} catch (e) {
|
||||
return 401;
|
||||
}
|
||||
const keyId = new URL(signature.keyId);
|
||||
const host = toPuny(keyId.hostname);
|
||||
if (await shouldBlockInstance(host, meta)) {
|
||||
return 403;
|
||||
}
|
||||
if (meta.privateMode && host !== config.host && host !== config.domain && !meta.allowedHosts.includes(host)) {
|
||||
return 403;
|
||||
}
|
||||
const keyIdLower = signature.keyId.toLowerCase();
|
||||
if (keyIdLower.startsWith("acct:")) {
|
||||
// Old keyId is no longer supported.
|
||||
return 401;
|
||||
}
|
||||
const dbResolver = new DbResolver();
|
||||
// HTTP-Signature keyIdを元にDBから取得
|
||||
let authUser = await dbResolver.getAuthUserFromKeyId(signature.keyId);
|
||||
// keyIdでわからなければ、resolveしてみる
|
||||
if (authUser == null) {
|
||||
try {
|
||||
keyId.hash = "";
|
||||
authUser = await dbResolver.getAuthUserFromApId(getApId(keyId.toString()));
|
||||
} catch (e) {
|
||||
// できなければ駄目
|
||||
return 403;
|
||||
}
|
||||
}
|
||||
// publicKey がなくても終了
|
||||
if (authUser?.key == null) {
|
||||
return 403;
|
||||
}
|
||||
// Cannot authenticate against local user
|
||||
if (authUser.user.uri === null || authUser.user.host === null) {
|
||||
return 400;
|
||||
}
|
||||
// Check if keyId hostname matches actor hostname
|
||||
if (toPuny(new URL(authUser.user.uri).hostname) !== host) {
|
||||
return 403;
|
||||
}
|
||||
// HTTP-Signatureの検証
|
||||
let httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem);
|
||||
// If signature validation failed, try refetching the actor
|
||||
if (!httpSignatureValidated) {
|
||||
authUser.key = await dbResolver.refetchPublicKeyForApId(authUser.user);
|
||||
if (authUser.key == null) {
|
||||
return 403;
|
||||
}
|
||||
httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem);
|
||||
}
|
||||
if (!httpSignatureValidated) {
|
||||
return 403;
|
||||
}
|
||||
if (!verifySignature(signature, authUser.key)) {
|
||||
return 401;
|
||||
}
|
||||
tickFetch();
|
||||
return 200;
|
||||
}
|
||||
return 200;
|
||||
}
|
||||
export async function getSignatureUser(req) {
|
||||
const signature = httpSignature.parseRequest(req, {
|
||||
headers: []
|
||||
});
|
||||
const keyId = new URL(signature.keyId);
|
||||
const dbResolver = new DbResolver();
|
||||
// Retrieve from DB by HTTP-Signature keyId
|
||||
const authUser = await dbResolver.getAuthUserFromKeyId(signature.keyId);
|
||||
if (authUser) {
|
||||
return authUser;
|
||||
}
|
||||
// Resolve if failed to retrieve by keyId
|
||||
keyId.hash = "";
|
||||
return await dbResolver.getAuthUserFromApId(getApId(keyId.toString()));
|
||||
}
|
||||
export function verifySignature(sig, key) {
|
||||
if (![
|
||||
'hs2019',
|
||||
'rsa-sha256'
|
||||
].includes(sig.algorithm.toLowerCase())) return false;
|
||||
try {
|
||||
return verify('rsa-sha256', Buffer.from(sig.signingString, 'utf8'), key.keyPem, Buffer.from(sig.params.signature, 'base64'));
|
||||
} catch {
|
||||
// Algo not supported
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export function verifyDigest(body, digest) {
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user