diff --git a/packages/cultmesh-browser/src/index.ts b/packages/cultmesh-browser/src/index.ts index 0398d313..ea37c17e 100644 --- a/packages/cultmesh-browser/src/index.ts +++ b/packages/cultmesh-browser/src/index.ts @@ -20,6 +20,7 @@ import { bytesToBase64, isLoopbackEndpoint, isUnsignedCertificate, + routeCertificateView, verifyAuthorityRoute, verifyProviderSessionProof, type CultMeshAuthorityIdentity, @@ -870,17 +871,7 @@ async function selectOdinRoute( protocolIds: route.protocolIds, priority: route.priority, generation: route.generation, - ...(route.certificate ? { certificate: { - providerKey: { - keyId: route.certificate.providerKeyId, - x: route.certificate.providerPublicKeyX, - y: route.certificate.providerPublicKeyY, - }, - odinKeyId: route.certificate.odinKeyId, - issuedAtUnixMilliseconds: route.certificate.issuedAtUnixMilliseconds, - expiresAtUnixMilliseconds: route.certificate.expiresAtUnixMilliseconds, - signature: route.certificate.signature, - } } : {}), + ...(route.certificate ? { certificate: routeCertificateView(route.certificate) } : {}), }; try { await verifyAuthorityRoute(candidate, trust); diff --git a/packages/cultnet-ts/src/cultmesh-authority.ts b/packages/cultnet-ts/src/cultmesh-authority.ts index 31cbcb61..c260cf70 100644 --- a/packages/cultnet-ts/src/cultmesh-authority.ts +++ b/packages/cultnet-ts/src/cultmesh-authority.ts @@ -6,7 +6,7 @@ // through the same functions; the C# reference is // `src/GameCult.Mesh/CultMeshAuthorityProof.cs`. -import type { CultMeshSessionOpenMessage } from "./contracts"; +import type { CultMeshRouteCertificateMessage, CultMeshSessionOpenMessage } from "./contracts"; export interface CultMeshAuthorityIdentity { verseId: string; @@ -36,6 +36,30 @@ export interface CultMeshAuthorityRouteCertificate { signature: string; } +/** + * Maps an advertised route's wire-shaped certificate to the view + * `verifyAuthorityRoute` and the transports consume. Shared by the QUIC + * realtime session manager (`cultmesh-ts`) and the browser WebSocket client + * (`cultmesh-browser`), which advertise the same `CultMeshAuthorityRouteMessage` + * certificate shape and were computing this mapping independently. + */ +export function routeCertificateView( + certificate: CultMeshRouteCertificateMessage | undefined, +): CultMeshAuthorityRouteCertificate | undefined { + if (!certificate) return undefined; + return { + providerKey: { + keyId: certificate.providerKeyId, + x: certificate.providerPublicKeyX, + y: certificate.providerPublicKeyY, + }, + odinKeyId: certificate.odinKeyId, + issuedAtUnixMilliseconds: certificate.issuedAtUnixMilliseconds, + expiresAtUnixMilliseconds: certificate.expiresAtUnixMilliseconds, + signature: certificate.signature, + }; +} + export type CultMeshAuthorityTrustMode = "authenticated-remote" | "local-development"; export interface CultMeshAuthorityTrustPolicy { diff --git a/packages/cultnet-ts/src/index.ts b/packages/cultnet-ts/src/index.ts index 073d2587..84bef769 100644 --- a/packages/cultnet-ts/src/index.ts +++ b/packages/cultnet-ts/src/index.ts @@ -73,6 +73,7 @@ export { isNullOrWhiteSpaceCSharp, isProtectedEndpoint, isUnsignedCertificate, + routeCertificateView, trimCSharp, verifyAuthorityRoute, verifyP256,