-
Notifications
You must be signed in to change notification settings - Fork 411
MSC4345: Server key identity and room membership #4345
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 8 commits
72f43b5
22b4a02
e127206
51bf46a
692e898
bb98f9d
13da6e9
710f081
128cf42
84738d9
d5187b0
a3c23e9
7bc2e7d
323de8a
d71bd43
fea8514
22c828a
390d71b
fda6b14
749e34e
d75af7c
742242e
e8d4640
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,278 @@ | ||
# MSC4345: Server key identity and room membership | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a fundamental fork-in-the-road choice here, when comparing this MSC to ones like MSC4243 (per-user keys). Both MSCs turn the DAG into a self-verifying data structure which is not reliant on DNS. The key difference is how they do this.
Neither MSC verifies the claimed domain for each server key: this is allowed to split brain which results in ugly user IDs appearing on clients ( The fork in the road is ultimately which direction the protocol wants to go:
Note that this proposal can support P2P, by adding another layer of keys on top of the server keys (which is what MSC4348 does). Federation protocols fundamentally need the server to know more information about their users, so it leaks more metadata than peer-to-peer protocols. For example, whilst this proposal can support P2P via MSC4348, it needs to know the PLs of all users on each server to know how to enforce the server participation event (the ambient power level in this MSC). Events also need to expose their sending node information in order to check the signatures on the "chaperone server/node". In contrast, P2P protocols can end up turning servers into store-and-forward nodes for encrypted data and that's it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is not true. Please stop misrepresenting this proposal. #4348 shows how P2P matrix would work within this proposal. And it is a cleaner solution that does not tie accounts to any domain or server, MSC4243 does. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please also consider that you have had months to develop 4243 behind a placeholder in private. With support and consultation from others. I've written this proposal very quickly solo. Without praise or encouragement. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I still extend an invitation to you to develop this MSC with me collaboratively.
Gnuxie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Events in Matrix are signed by the sending server's domain-scoped | ||
sigining key, which is also a rotating key. During signature | ||
verification, a server must obtain the public key used to sign an | ||
event. This presents problems when the origin server is offline or has | ||
been decommissioned. As this forces servers to rely on notaries to | ||
supply historical keys. | ||
|
||
There are several issues with this system that lead to inconsistent | ||
views of the DAG: | ||
|
||
- Centralisation of trust: Signature verification depends on notaries | ||
being online and honest about historical keys. | ||
|
||
- Fragility: Notaries may never have been present in the rooms that a | ||
server is trying to join. This is especially true of matrix.org | ||
which is the notary used by default in synapse. If no notary has the | ||
key history for a given server, none of the events can be verified. | ||
|
||
- Complexity and insecurity: Verifying authenticity of events is an | ||
unnecessarily crossed concern with verification of ownership of a | ||
domain. | ||
Gnuxie marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
This MSC is inspired the work of @kegsay in | ||
[MSC4243](https://github.com/matrix-org/matrix-spec-proposals/pull/4243). | ||
|
||
## Proposal | ||
|
||
|
||
We propose to tie the server's identity within a room to a long lived | ||
ed25519 public key. This key is explicitly appended to the DAG via an | ||
auth event. This event also sets the terms for routing information and | ||
the server's participation within the room. | ||
|
||
Therefore the DAG itself becomes a record of which keys were held by | ||
participants, which eliminates the need for notaries in public key | ||
discovery. | ||
Gnuxie marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
In addition, servers are unable to participate within a room until | ||
their key has been added by an existing participant. This allows for | ||
current participants to verify ownership of a domain before | ||
Gnuxie marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
participation is permitted. | ||
Gnuxie marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
We also introduce server participation, which allows servers to be | ||
denied access to the room at the DAG level, and we also introduce | ||
rules that allow servers to be removed without the need for | ||
soft-failure. | ||
Gnuxie marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
This allows both public and private rooms to benefit from DAG | ||
reproducibility and preemptive access control for servers without the | ||
use of a policy server. | ||
|
||
### The `m.server.participation` state event, `state_key: ${origin_server_key}` | ||
Gnuxie marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We still need to steal more prose from MSC4243 to describe the exact format for the server key and then use that consistently. |
||
|
||
#### The `advertised_domain` property | ||
|
||
This is a string representing the domain of the server. This is not an | ||
attestation that ownership has been verified by the sender of the | ||
event. This property is protected from redaction. | ||
|
||
This property is not required, as it may be desirable to hide the | ||
domain when setting the server's participation to `denied`. Particularly | ||
in the event of attempted impersonation or an abusive domain name. | ||
|
||
#### The `participation` property | ||
|
||
`participation` can be one of `permitted`, `accepted` or | ||
`denied`. `participation` is protected from redaction. | ||
Gnuxie marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Gnuxie marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
A denied server must not be sent a `m.server.participation` event | ||
unless the targeted server is already present within the room. This is | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you're just stating existing behaviour (we don't tell servers who aren't in the room that we're talking about them)? If so, it's really confusing because it makes it seem like this is a really important part of the property, instead of just some edge case handling? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wasn't aware that this was existing behaviour. Even if it is, it seems like it is a really important part of the property right? |
||
to prevent malicious servers being made aware of rooms that they have | ||
not yet discovered. | ||
|
||
#### The `reason` property | ||
|
||
An optional reason property may be present in order to explain the | ||
reason why a server has been denied or permitted to participate. | ||
|
||
Gnuxie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
### Terminology for authorization | ||
|
||
The _considered event extremities_ is the set of events provided by | ||
`prev_events` and `auth_events` of the considered event. | ||
Gnuxie marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
The _considered event's acknowledged events_ is the set of events | ||
connected to the `prev_events` of the considered event. | ||
Gnuxie marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
The _origin server's acknowledged extremities_ is the set of events | ||
that are the tips of all DAG fragments which the origin server has | ||
previously referenced in the `prev_events` of any event that has | ||
already been authorized. This set is empty if the origin server has | ||
not sent any prior events to the room. | ||
|
||
The _origin server's acknowledged events_ is the set of events that | ||
are connected to the _origin server's acknowledged extremities_ set, | ||
including the _acknowledged extremities_ themselves. | ||
|
||
### Key revocation | ||
|
||
We define a _key revocation event_ to be an `m.server.participation` | ||
event with the following properties: | ||
|
||
1. The event's signature can be verified with the key found in the `state_key`. | ||
2. The event's `participation` is `denied`. | ||
3. The _considered event's acknowledged events_ is not a subset of the | ||
_origin server's acknowledged events_. | ||
Gnuxie marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
4. If the current `participation` is `denied`: | ||
Gnuxie marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
1. If the current `participation` is not signed with the same key. | ||
Gnuxie marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
2. The _considered event's acknowledged events_ is equal to the | ||
the _origin server's acknowledged events_. | ||
|
||
### The `m.server.participation` authorization rule | ||
|
||
These rules are to be inserted before rule 4 in [version | ||
12](https://spec.matrix.org/v1.10/rooms/v11/#authorization-rules), the | ||
check for `m.room.member`. | ||
|
||
1. If type is `m.server.participation`: | ||
1. If the sender's signature matches the `state_key` of the | ||
considered event: | ||
1. If the `participation` field of the considered event is | ||
Gnuxie marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
`denied`, allow. | ||
1. If the `participation` field of the considered event is not | ||
`accepted`, reject. | ||
1. If the sender is a room owner, allow. | ||
1. If the current participation state for the target is `permitted` | ||
or `accepted`, allow. | ||
1. Otherwise, reject. | ||
2. If the `sender`'s current participation state is not `accepted`, reject. | ||
3. If `participation` is `accepted`, reject[^participation-accept]. | ||
kegsay marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Gnuxie marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
4. If there is no current participation state for the target: | ||
2. If `partcipation` is `denied`: | ||
1. If the `sender`'s power level is greater than or equal to the _ban level_, | ||
is greater than or equal to the target server's ambient power level, allow. | ||
2. Otherwise, reject. | ||
3. If `participation` is `permitted`: | ||
1. If the _target server_'s current participation state is `accepted`, reject. | ||
4. If the _target server_'s current participation state is `denied`: | ||
1. If the origin of the current participation state is the target key, reject[^revocation]. | ||
2. If the `sender`'s power level is less than the _ban | ||
level_ or is less than the target server's ambient power | ||
level, reject. | ||
5. if the `sender`'s power level is greater than or equal to | ||
the _invite level_, allow. 3. Otherwise, reject. | ||
|
||
|
||
5. If the `sender`'s current participation state is not `accepted`, reject. | ||
|
||
[^participation-accept]: | ||
This rule prevents anyone but the owner of | ||
the key from setting the participation to accept | ||
|
||
### The authorization rule for `denied` participation | ||
|
||
This rule should be inserted at the beginning of auth rules and noted | ||
in the description of soft failure | ||
https://spec.matrix.org/latest/server-server-api/#soft-failure. | ||
|
||
1. If the `sender`'s current participation is `denied`: | ||
1. If the considered event is a _key revocation event_, allow[^revocation]. | ||
2. If the the _current participation_ event's _origin server's | ||
acknowledged events_ does not include the considered event, reject. | ||
3. Fall-through. | ||
|
||
This rule exists to ensure that a consistent history is provided for | ||
the _denied server_. It removes the avenue for the denied server to | ||
Gnuxie marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
reference stale state to append an infinite number of soft failed | ||
events to the DAG. It also prevents the sender of the deny event from | ||
Gnuxie marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
placing the deny earlier in history to remove the target server's | ||
events. Doing so will have the same effect as using the current | ||
state. | ||
|
||
[^revocation]: | ||
This rule enforces that the owner of the key has total | ||
autonomy over its revocation. Room admins cannot steal a key and | ||
override this, and even if server admins set the server to deny, | ||
the key owner can still revoke the key. | ||
|
||
### The `/request_participation` endpoint | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The subtext for this endpoint is that the requested server needs to create the participation event with one of its users. There may be objections to this, but in terms of client UI we're close enough with restricted join already in that the event says "bob joined via alice's server". So i'm not sure that argument alone can stand. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Needs reworking see #4345 (comment) |
||
|
||
When a server requests participation, the requested server should | ||
verify that the joining server is claiming ownership of the provided | ||
server key. The request should also be signed using the same server key. | ||
|
||
Then, the requested server will emit an `m.server.participation` event | ||
into the room with the key and the `advertised_domain` property filled | ||
for the request origin. | ||
|
||
Once this is complete, the requested server will respond with the information | ||
required to begin interacting with the room. | ||
|
||
When the joining server gets this response, it should immediately | ||
change its own participation to `accepted` in order to prevent users | ||
from overwriting the `advertised_domain`. | ||
|
||
The following endpoint is defined: GET `/_matrix/federation/v1/request_participation/{roomId}/{serverKey}`. | ||
|
||
The following query parameters are supported: | ||
|
||
- `ver` the room versions the sending server has support for (identical to `make_join`). | ||
|
||
- `omit_members` whether to omit members from the response (identical to `send_join`). | ||
|
||
The response is identical to `send_join`. | ||
|
||
### Changes to `/_matrix/key/v3/query` | ||
|
||
`valid_until_ts` is removed. Keys are never time-bounded and | ||
revocation is explicit via DAG state. | ||
|
||
### Changes to the user ID format | ||
|
||
- User ID _server name_'s are replaced with an ed25519 public key, | ||
called the _server key_[^msc4243-prose]. | ||
|
||
- The private key for this _server key_ signs event JSON over federation[^msc4243-prose]. | ||
|
||
[^msc4243-prose]: | ||
This wording is taken directly from MSC4243 and | ||
shaped up a little | ||
|
||
### Impositions on Client UI | ||
|
||
Homeserver's must verify domain ownership before events are annotated | ||
with `unsigned.server_domain`. Clients then use this to show a user's | ||
server domain user ID rather than a user's server key user ID. Clients | ||
should never use the `m.server.participation` `advertised_domain` to | ||
show the origin of events. | ||
|
||
Clients should encode the public key for displaying unverified | ||
servers. Clients may also highlight this by deriving a stable colour | ||
identity from the key. | ||
|
||
Please suggest specific algorithms to make this consistent. | ||
|
||
## Potential issues | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Server implementers should only allow server admins to revoke keys, and shouldn't let any user of theirs in a room to do it.
Gnuxie marked this conversation as resolved.
Show resolved
Hide resolved
Gnuxie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Alternatives | ||
|
||
### MSC4243: User ID localparts as Account Keys | ||
|
||
This proposal is an alternative to [MSC4243: User ID localparts as | ||
Account | ||
Keys](https://github.com/matrix-org/matrix-spec-proposals/pull/4243) | ||
and borrows several ideas from the same proposal. It is not required | ||
reading. The key difference between these proposals is that this | ||
proposal describes long lived identity for servers as a key pair in | ||
Matrix rooms. Whereas MSC4243 only does so for individual user | ||
accounts. | ||
|
||
However, critically this MSC provides traceability to the origin of | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this actually true? Nothing is verified in this proposal, so as per my sleeper server example you still have to play whack-a-mole with any malicious server. The only advantage this proposal provides is the implicit invite to public rooms, so you could feasibly do some investigation to see which server is letting in these other servers. It's probably not safe to actually take action against those servers though because that could be weaponsied (e.g I make a domain and valid server, I join via the victim server, then nuke the domain and spam freely, such that when investigators see who invited me they think the victim server is colluding when it isn't). The exciting part of this proposal to me was the idea that this would provide better traceability to the origin of users, but sadly I just don't see how this proposal does this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So let's be specific. The sleeper server would have to have the invite permission. So the rooms where this can happen are going to be ones set to invite only AND where it is a choice to have the invite be the default power level (this is true for rooms created with the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps we should break away from the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think you'd hold the same standard for the invite power level in private rooms though? Since currently this same attack can happen in all matrix rooms where invite is default? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
@kegsay I don't understand this bit. Why would investigators conclude that spam events are originating from the victim server? What evidence would they use to conclude that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I never said originating, I said colluding. The victim server "let in" the spam server. |
||
usrs, whereas MSC4243 does not unless a policy server is in use to | ||
sign each event. | ||
|
||
### MSC4124: Simple Server Authorization | ||
|
||
This proposal borrows the principle of constrained server membership | ||
from MSC4124. Specifically changing authorization to stop | ||
unencountered servers from suddenly appending an infinite amount of | ||
data to the DAG. | ||
|
||
### MSC4104: Auth Lock: Soft-failure-be-gone! | ||
|
||
This proposal encodes a special auth rule for `denied` participation to | ||
avoid soft failure and the problems discussed in MSC4104. | ||
|
||
## Security considerations | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please allow keys to be scoped to a number of events. E.g. 1000 before rotation is required. See also #4353 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably worth requiring that authorization events have a separate limit to normal events. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably worth doing the hard work of specifying keys as capabilities omg. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the awesome secure Matrix you can have! But you don't! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is probably scope creep and should be a follow up MSC, see the-draupnir-project/planning#51 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should add a note in alternatives or issues before closing the thread. |
||
|
||
See [Impositions on client UI](#impositions-on-client-ui). | ||
|
||
## Unstable prefix | ||
|
||
`m.server.participation` -> `org.matrix.msc4345.participation` | ||
|
||
`_matrix` => `_matrix/msc4345/` or whatever the norm is here. | ||
|
||
## Dependencies | ||
|
||
None. |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implementation requirements: