wire-mesh-core 1.38.0 → 1.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -61,7 +61,8 @@ function createRoomRekeyHandler(options) {
61
61
  clock: options.clock,
62
62
  revocation: options.revocation,
63
63
  expectedBearer: options.identity.deviceId,
64
- roomPath
64
+ roomPath,
65
+ dmRootPolicy: "either-participant"
65
66
  });
66
67
  if (!verdict.ok) {
67
68
  await incoming.respond({
@@ -60,7 +60,8 @@ function createRoomRekeyHandler(options) {
60
60
  clock: options.clock,
61
61
  revocation: options.revocation,
62
62
  expectedBearer: options.identity.deviceId,
63
- roomPath
63
+ roomPath,
64
+ dmRootPolicy: "either-participant"
64
65
  });
65
66
  if (!verdict.ok) {
66
67
  await incoming.respond({
@@ -28,8 +28,18 @@ async function verifyRoomToken(token, options) {
28
28
  reason: "wrong_scope_path"
29
29
  };
30
30
  const parsed = require_domain_room_path.parseRoomPath(options.roomPath);
31
- const expectedRootHex = parsed.kind === "owner-named" ? parsed.owner : require_domain_device_id.deviceIdToHex(options.identity.deviceId);
32
- if (require_domain_device_id.deviceIdToHex(verdict.rootIssuer) !== expectedRootHex) return {
31
+ const rootHex = require_domain_device_id.deviceIdToHex(verdict.rootIssuer);
32
+ if (parsed.kind === "owner-named") {
33
+ if (rootHex !== parsed.owner) return {
34
+ ok: false,
35
+ reason: "wrong_chain_root"
36
+ };
37
+ } else if (options.dmRootPolicy === "either-participant") {
38
+ if (rootHex !== parsed.participants[0] && rootHex !== parsed.participants[1]) return {
39
+ ok: false,
40
+ reason: "wrong_chain_root"
41
+ };
42
+ } else if (rootHex !== require_domain_device_id.deviceIdToHex(options.identity.deviceId)) return {
33
43
  ok: false,
34
44
  reason: "wrong_chain_root"
35
45
  };
@@ -1,2 +1,2 @@
1
- import { a as verifyRoomToken, i as VerifyRoomTokenOptions, n as RoomTokenVerdict, r as RoomTokenVerdictReason, t as ROOM_MEMBER_CAPABILITY } from "../room-token-verification-BY9AwZQ-.cjs";
1
+ import { a as verifyRoomToken, i as VerifyRoomTokenOptions, n as RoomTokenVerdict, r as RoomTokenVerdictReason, t as ROOM_MEMBER_CAPABILITY } from "../room-token-verification-CkQV0Iq7.cjs";
2
2
  export { ROOM_MEMBER_CAPABILITY, RoomTokenVerdict, RoomTokenVerdictReason, VerifyRoomTokenOptions, verifyRoomToken };
@@ -1,2 +1,2 @@
1
- import { a as verifyRoomToken, i as VerifyRoomTokenOptions, n as RoomTokenVerdict, r as RoomTokenVerdictReason, t as ROOM_MEMBER_CAPABILITY } from "../room-token-verification-CYKEP_h6.mjs";
1
+ import { a as verifyRoomToken, i as VerifyRoomTokenOptions, n as RoomTokenVerdict, r as RoomTokenVerdictReason, t as ROOM_MEMBER_CAPABILITY } from "../room-token-verification-BggPfYr8.mjs";
2
2
  export { ROOM_MEMBER_CAPABILITY, RoomTokenVerdict, RoomTokenVerdictReason, VerifyRoomTokenOptions, verifyRoomToken };
@@ -27,8 +27,18 @@ async function verifyRoomToken(token, options) {
27
27
  reason: "wrong_scope_path"
28
28
  };
29
29
  const parsed = parseRoomPath(options.roomPath);
30
- const expectedRootHex = parsed.kind === "owner-named" ? parsed.owner : deviceIdToHex(options.identity.deviceId);
31
- if (deviceIdToHex(verdict.rootIssuer) !== expectedRootHex) return {
30
+ const rootHex = deviceIdToHex(verdict.rootIssuer);
31
+ if (parsed.kind === "owner-named") {
32
+ if (rootHex !== parsed.owner) return {
33
+ ok: false,
34
+ reason: "wrong_chain_root"
35
+ };
36
+ } else if (options.dmRootPolicy === "either-participant") {
37
+ if (rootHex !== parsed.participants[0] && rootHex !== parsed.participants[1]) return {
38
+ ok: false,
39
+ reason: "wrong_chain_root"
40
+ };
41
+ } else if (rootHex !== deviceIdToHex(options.identity.deviceId)) return {
32
42
  ok: false,
33
43
  reason: "wrong_chain_root"
34
44
  };
@@ -2,7 +2,7 @@ import { Dt as RoomPath, Et as RoomNoticeClaims, Tt as RoomNotice, p as Capabili
2
2
  import { t as IdentityPort } from "../identity-B4XBWkqg.cjs";
3
3
  import { t as Clock } from "../clock-DiSx-WKM.cjs";
4
4
  import { a as RevocationCheck } from "../tokens-C9Kwy0Gk.cjs";
5
- import { r as RoomTokenVerdictReason } from "../room-token-verification-BY9AwZQ-.cjs";
5
+ import { r as RoomTokenVerdictReason } from "../room-token-verification-CkQV0Iq7.cjs";
6
6
  //#region src/domain/room.d.ts
7
7
  export type RoomNoticeVerdictReason = "malformed" | "wrong_room" | "bad_signature" | "wrong_poster" | "content_expired" | "key_epoch_mismatch" | RoomTokenVerdictReason;
8
8
  export type RoomNoticeVerdict = {
@@ -2,7 +2,7 @@ import { Dt as RoomPath, Et as RoomNoticeClaims, Tt as RoomNotice, p as Capabili
2
2
  import { t as IdentityPort } from "../identity-CIb9dGkJ.mjs";
3
3
  import { t as Clock } from "../clock-DiSx-WKM.mjs";
4
4
  import { a as RevocationCheck } from "../tokens-k172l0Iy.mjs";
5
- import { r as RoomTokenVerdictReason } from "../room-token-verification-CYKEP_h6.mjs";
5
+ import { r as RoomTokenVerdictReason } from "../room-token-verification-BggPfYr8.mjs";
6
6
  //#region src/domain/room.d.ts
7
7
  export type RoomNoticeVerdictReason = "malformed" | "wrong_room" | "bad_signature" | "wrong_poster" | "content_expired" | "key_epoch_mismatch" | RoomTokenVerdictReason;
8
8
  export type RoomNoticeVerdict = {
@@ -18,6 +18,21 @@ interface VerifyRoomTokenOptions extends Omit<VerifyCapabilityTokenOptions, "exp
18
18
  expectedBearer: DeviceId;
19
19
  /** The room path this request claims to act on (obligation 3) -- must equal the token's own scope.path, and its own shape determines the chain root obligation 1 requires. */
20
20
  roomPath: string;
21
+ /**
22
+ * Whose root a DM token's chain may terminate at. "self" (the default) is
23
+ * the room.send-grade rule: the verifier itself, never either named
24
+ * participant directly, since a DM token minted by anyone else would let a
25
+ * sender self-issue authority to message a stranger unsolicited.
26
+ * "either-participant" relaxes it to EITHER device named in the DM path --
27
+ * valid only where the caller's own security does not rest on the root rule
28
+ * itself. room.rekey is that caller: in a DM each participant's own held
29
+ * token chains to the OTHER side (I approve your join, minting a grant
30
+ * rooted at me, held by you), so the strict rule can never pass, and the
31
+ * handler's actual binding is the ECDH unwrap -- a forged rekey needs the
32
+ * root's private key to produce unwrappable ciphertext -- not the root
33
+ * check. A root outside the path is refused under either policy.
34
+ */
35
+ dmRootPolicy?: "self" | "either-participant";
21
36
  }
22
37
  /**
23
38
  * Verifies a `room:member` capability token against all six of core/room's verifier obligations. Delegates obligations 2/4/5 to verifyCapabilityToken directly; adds obligation 3 (scope.kind/scope.path must match the room being acted on) and obligation 1 (the chain's root must be the room's own owner for a named room, or the verifying identity itself for a DM -- never either named participant directly, since a DM token minted by anyone other than the verifier would let a sender self-issue authority to message a stranger unsolicited).
@@ -18,6 +18,21 @@ interface VerifyRoomTokenOptions extends Omit<VerifyCapabilityTokenOptions, "exp
18
18
  expectedBearer: DeviceId;
19
19
  /** The room path this request claims to act on (obligation 3) -- must equal the token's own scope.path, and its own shape determines the chain root obligation 1 requires. */
20
20
  roomPath: string;
21
+ /**
22
+ * Whose root a DM token's chain may terminate at. "self" (the default) is
23
+ * the room.send-grade rule: the verifier itself, never either named
24
+ * participant directly, since a DM token minted by anyone else would let a
25
+ * sender self-issue authority to message a stranger unsolicited.
26
+ * "either-participant" relaxes it to EITHER device named in the DM path --
27
+ * valid only where the caller's own security does not rest on the root rule
28
+ * itself. room.rekey is that caller: in a DM each participant's own held
29
+ * token chains to the OTHER side (I approve your join, minting a grant
30
+ * rooted at me, held by you), so the strict rule can never pass, and the
31
+ * handler's actual binding is the ECDH unwrap -- a forged rekey needs the
32
+ * root's private key to produce unwrappable ciphertext -- not the root
33
+ * check. A root outside the path is refused under either policy.
34
+ */
35
+ dmRootPolicy?: "self" | "either-participant";
21
36
  }
22
37
  /**
23
38
  * Verifies a `room:member` capability token against all six of core/room's verifier obligations. Delegates obligations 2/4/5 to verifyCapabilityToken directly; adds obligation 3 (scope.kind/scope.path must match the room being acted on) and obligation 1 (the chain's root must be the room's own owner for a named room, or the verifying identity itself for a DM -- never either named participant directly, since a DM token minted by anyone other than the verifier would let a sender self-issue authority to message a stranger unsolicited).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wire-mesh-core",
3
- "version": "1.38.0",
3
+ "version": "1.40.0",
4
4
  "type": "module",
5
5
  "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c",
6
6
  "repository": {