Skip to content

Commit

Permalink
make basic pdu validations and unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ggazzo committed Dec 16, 2024
1 parent 348dd89 commit cca3e2c
Show file tree
Hide file tree
Showing 6 changed files with 529 additions and 88 deletions.
61 changes: 9 additions & 52 deletions packages/homeserver/src/plugins/validateHeaderSignature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import {
} from "../signJson";
import { isConfigContext } from "./isConfigContext";
import { isMongodbContext } from "./isMongodbContext";
import { makeGetPublicKeyFromServerProcedure } from "../procedures/getPublicKeyFromServer";
import {
getPublicKeyFromRemoteServer,
makeGetPublicKeyFromServerProcedure,
} from "../procedures/getPublicKeyFromServer";
import { makeRequest } from "../makeRequest";
import { ForbiddenError, UnknownTokenError } from "../errors";
import { extractURIfromURL } from "../helpers/url";
Expand Down Expand Up @@ -68,59 +71,13 @@ export const validateHeaderSignature = async ({

const getPublicKeyFromServer = makeGetPublicKeyFromServerProcedure(
context.mongo.getValidPublicKeyFromLocal,
async () => {
const result = await makeRequest({
method: "GET",
domain: origin.origin,
uri: "/_matrix/key/v2/server",
signingName: context.config.name,
});
if (result.valid_until_ts < Date.now()) {
throw new Error("Expired remote public key");
}

const [signature] = await getSignaturesFromRemote(
result,
() =>
getPublicKeyFromRemoteServer(
origin.origin,
);

const [, publickey] =
Object.entries(result.verify_keys).find(
([key]) => key === origin.key,
) ?? [];

if (!publickey) {
throw new Error("Public key not found");
}

if (!signature) {
throw new Error(`Signatures not found for ${origin.origin}`);
}

if (
!(await verifyJsonSignature(
result,
origin.origin,
Uint8Array.from(atob(signature.signature), (c) => c.charCodeAt(0)),
Uint8Array.from(atob(publickey.key), (c) => c.charCodeAt(0)),
signature.algorithm,
signature.version,
))
) {
throw new Error("Invalid signature");
}

const [algorithm, version] = origin.key.split(":");

if (!isValidAlgorithm(algorithm)) {
throw new Error("Invalid algorithm");
}
origin.destination,
origin.key,
),

return {
key: publickey.key,
validUntil: result.valid_until_ts,
};
},
context.mongo.storePublicKey,
);

Expand Down
73 changes: 71 additions & 2 deletions packages/homeserver/src/procedures/getPublicKeyFromServer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
import { makeRequest } from "../makeRequest";
import {
getSignaturesFromRemote,
isValidAlgorithm,
verifyJsonSignature,
} from "../signJson";

export const makeGetPublicKeyFromServerProcedure = (
getFromLocal: (origin: string, key: string) => Promise<string | undefined>,
getFromOrigin: (origin: string) => Promise<{ key: string; validUntil: number }>,
store: (origin: string, key: string, value: string, validUntil: number) => Promise<void>,
getFromOrigin: (
origin: string,
) => Promise<{ key: string; validUntil: number }>,
store: (
origin: string,
key: string,
value: string,
validUntil: number,
) => Promise<void>,
) => {
return async (origin: string, key: string) => {
const localPublicKey = await getFromLocal(origin, key);
Expand All @@ -18,3 +32,58 @@ export const makeGetPublicKeyFromServerProcedure = (
throw new Error("Public key not found");
};
};

export const getPublicKeyFromRemoteServer = async (
domain: string,
signingName: string,
algorithmAndVersion: string,
) => {
const result = await makeRequest({
method: "GET",
domain,
uri: "/_matrix/key/v2/server",
signingName,
});
if (result.valid_until_ts < Date.now()) {
throw new Error("Expired remote public key");
}

const [signature] = await getSignaturesFromRemote(result, domain);

const [, publickey] =
Object.entries(result.verify_keys).find(
([key]) => key === algorithmAndVersion,
) ?? [];

if (!publickey) {
throw new Error("Public key not found");
}

if (!signature) {
throw new Error(`Signatures not found for ${domain}`);
}

if (
!(await verifyJsonSignature(
result,
domain,
Uint8Array.from(atob(signature.signature), (c) => c.charCodeAt(0)),
Uint8Array.from(atob(publickey.key), (c) => c.charCodeAt(0)),
signature.algorithm,
signature.version,
))
) {
throw new Error("Invalid signature");
}

const [algorithm, version] = algorithmAndVersion.split(":");

if (!isValidAlgorithm(algorithm)) {
throw new Error("Invalid algorithm");
}

return {
key: publickey.key,
validUntil: result.valid_until_ts,
};
};
23 changes: 15 additions & 8 deletions packages/homeserver/src/routes/federation/sendInviteV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,21 @@ export const sendInviteV2Route = new Elysia().put(
event: responseBody.event,
});
}
if (responseBody.state?.length) {
await eventsCollection.insertMany(
responseBody.state.map((event) => ({
_id: generateId(event),
event,
})),
);
}
await Promise.all(
responseBody.state?.map((event) => {
const promise = eventsCollection
.insertOne({
_id: generateId(event),
event,
})
.catch((e) => {
// TODO events failing because of duplicate key
// the reason is that we are saving the event on invite event
console.error("error saving event", e, event);
});
return promise;
}) ?? [],
);
}, 1000);

return { event: body.event };
Expand Down
Loading

0 comments on commit cca3e2c

Please sign in to comment.