Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 40 additions & 6 deletions lib/Protocol/Matrix.pm
Original file line number Diff line number Diff line change
Expand Up @@ -266,18 +266,50 @@ my %ALLOWED_CONTENT_BY_TYPE = (

sub redact_event
{
my ( $event ) = @_;
my ( $event, $room_version ) = @_;
$room_version //= 1;

defined( my $type = $event->{type} ) or
croak "Event requires a 'type'";

delete $event->{redacts};
my $old_content = delete $event->{content};
my $old_unsigned = delete $event->{unsigned};

$ALLOWED_KEYS{$_} or delete $event->{$_} for keys %$event;

my $new_content = $event->{content} = {};

$old_content //= {};
$old_unsigned //= {};

# Room version 11+ uses updated redaction rules:
# - m.room.create: entire content property is preserved
# - m.room.power_levels: 'invite' is also preserved
# - m.room.member: 'third_party_invite.signed' is also preserved
# - m.room.redaction: 'redacts' is also preserved
# Non-numeric (unstable) room versions are assumed to be 11+
if( $room_version !~ /\A[0-9]+\z/ or $room_version >= 11 ) {
if( $type eq 'm.room.create' ) {
%$new_content = %$old_content;
$event->{unsigned}{age_ts} = $old_unsigned->{age_ts} if exists $old_unsigned->{age_ts};
return;
}
if( $type eq 'm.room.power_levels' ) {
$new_content->{invite} = $old_content->{invite} if exists $old_content->{invite};
}
if( $type eq 'm.room.member' ) {
my $tpi = $old_content->{third_party_invite};
if( ref $tpi eq 'HASH' && exists $tpi->{signed} ) {
$new_content->{third_party_invite} = { signed => $tpi->{signed} };
}
}
if( $type eq 'm.room.redaction' ) {
# In v11, 'redacts' moved into content; preserve content.redacts
$new_content->{redacts} = $old_content->{redacts} if exists $old_content->{redacts};
}
}

if( my $allowed_content_keys = $ALLOWED_CONTENT_BY_TYPE{$type} ) {
exists $old_content->{$_} and $new_content->{$_} = $old_content->{$_} for
@$allowed_content_keys;
Expand All @@ -288,8 +320,8 @@ sub redact_event

sub redacted_event
{
my ( $event ) = @_;
redact_event( $event = { %$event } );
my ( $event, $room_version ) = @_;
redact_event( $event = { %$event }, $room_version );
return $event;
Comment thread
FrenchGithubUser marked this conversation as resolved.
}

Expand All @@ -309,6 +341,7 @@ sub sign_event_json

my $origin = $args{origin} or croak "Require an 'origin'";
my $key_id = $args{key_id} or croak "Require a 'key_id'";
my $room_version = delete $args{room_version};

# 'hashes' records the original unredacted version
{
Expand All @@ -319,7 +352,7 @@ sub sign_event_json
}

# Signature is of redacted version
sign_json( my $signed = redacted_event( $event ), %args );
sign_json( my $signed = redacted_event( $event, $room_version ), %args );

$event->{signatures} = $signed->{signatures};
}
Expand Down Expand Up @@ -349,9 +382,10 @@ sub signed_event_json

sub verify_event_json_signature
{
my ( $event, @args ) = @_;
my ( $event, %args ) = @_;

verify_json_signature( redacted_event( $event ), @args );
my $room_version = delete $args{room_version};
verify_json_signature( redacted_event( $event, $room_version ), %args );
}

=head1 AUTHOR
Expand Down
40 changes: 37 additions & 3 deletions lib/SyTest/Federation/AuthChecks.pm
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,12 @@ sub auth_check_event
$accepted_events, $event->{auth_events}, "m.room.create"
);

my $room_creator = _creator_for_create_event( $create_event );
return 0 unless defined $room_creator;

{
users => {
$create_event->{content}{creator} => 100,
$room_creator => 100,
},
users_default => 0,

Expand Down Expand Up @@ -86,6 +89,35 @@ sub auth_check_event
return 1;
}

=head2 _creator_for_create_event

my $creator = _creator_for_create_event( $create_event )

Returns the creator MXID for a given C<m.room.create> event. For room
version 11+, the creator is taken from the C<sender> field; for older
versions it is taken from C<content.creator>.

=cut

sub _creator_for_create_event
{
my ( $create_event ) = @_;

$create_event or
return undef;

my $room_version = $create_event->{content}{room_version};

# For room version 11+ (or unstable/non-numeric versions), 'creator' is absent
# from content; use sender instead.
if( defined $room_version && ( $room_version !~ /\A[0-9]+\z/ || $room_version >= 11 ) ) {
return $create_event->{sender};
}

# For older room versions, 'creator' must be present explicitly.
return $create_event->{content}{creator};
}

sub auth_check_event_m_room_create
{
my $self = shift;
Expand All @@ -96,7 +128,8 @@ sub auth_check_event_m_room_create
return 0;

# Any m.room.create event is acceptable, provided that the creator matches
return $event->{sender} eq $event->{content}{creator};
my $creator = _creator_for_create_event( $event );
return defined( $creator ) and $event->{sender} eq $creator;
}

sub auth_check_event_m_room_member
Expand All @@ -117,7 +150,8 @@ sub auth_check_event_m_room_member
$accepted_events, $event->{auth_events}, "m.room.create"
);

if( $create_event and $event->{state_key} eq $create_event->{content}{creator} ) {
my $creator = _creator_for_create_event( $create_event );
if( $creator and $event->{state_key} eq $creator ) {
return 1;
}

Expand Down
13 changes: 7 additions & 6 deletions lib/SyTest/Federation/Datastore.pm
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,13 @@ the C<signatures> key.
sub sign_event
{
my $self = shift;
my ( $event ) = @_;
my ( $event, %args ) = @_;

sign_event_json( $event,
secret_key => $self->secret_key,
origin => $self->server_name,
key_id => $self->key_id,
secret_key => $self->secret_key,
origin => $self->server_name,
key_id => $self->key_id,
room_version => $args{room_version},
);
Comment thread
FrenchGithubUser marked this conversation as resolved.
}

Expand Down Expand Up @@ -204,12 +205,12 @@ sub create_event
$event_id = $self->next_event_id( $event_id_suffix );
$event->{event_id} = $event_id;
}
$self->sign_event( $event );
$self->sign_event( $event, room_version => $room_version );
} else {
die "event with explicit event_id in room v$room_version"
if defined $event_id;

$self->sign_event( $event );
$self->sign_event( $event, room_version => $room_version );
$event_id = id_for_event( $event, $room_version );
}

Expand Down
7 changes: 4 additions & 3 deletions lib/SyTest/Federation/Protocol.pm
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ Calculates the reference hash of an event.

sub hash_event
{
my ( $event ) = @_;
my ( $event, $room_version ) = @_;
$room_version //= 1;
croak "Require an event" unless ref $event eq 'HASH';
my $redacted = redacted_event( $event );
my $redacted = redacted_event( $event, $room_version );
Comment on lines 45 to +50
delete $redacted->{signatures};
delete $redacted->{age_ts};
delete $redacted->{unsigned};
Expand Down Expand Up @@ -76,7 +77,7 @@ sub id_for_event
return $event_id;
}

my $event_hash = hash_event( $event );
my $event_hash = hash_event( $event, $room_version );

# room v3 uses the unpadded-base64-encoded hash
if( $room_version eq '3' ) {
Expand Down
13 changes: 9 additions & 4 deletions lib/SyTest/Federation/Room.pm
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,18 @@ sub create_initial_events
$self->room_version eq "1" ? undef : $self->room_version
);

my $create_content = {
defined( $room_version ) ? ( room_version => $room_version ) : (),
};
# Default to old 'creator' field if no room version is specified, or room version is
# a numeric value <11. Non-numeric (unstable) versions are treated as 11+.
$create_content->{creator} = $creator
unless defined( $room_version ) && ( $room_version !~ /\A[0-9]+\z/ || $room_version >= 11 );

$self->create_and_insert_event(
type => "m.room.create",

content => {
creator => $creator,
defined( $room_version ) ? ( room_version => $room_version ) : (),
},
content => $create_content,
sender => $creator,
state_key => "",
);
Expand Down
16 changes: 13 additions & 3 deletions tests/30rooms/01state.pl
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,19 @@
$event->{sender} eq $user->user_id or
die "Expected user_id to be ${\$user->user_id}";

assert_json_keys( my $content = $event->{content}, qw( creator ));
$content->{creator} eq $user->user_id or
die "Expected creator to be ${\$user->user_id}";
my $content = $event->{content};
my $room_version = $content->{room_version} // "1";
if( $room_version !~ /\A[0-9]+\z/ || $room_version >= 11 ) {
# Room version 11+: 'creator' must be absent from content.
exists $content->{creator} and
die "Expected no 'creator' key in content for room version $room_version";
} else {
# Older room versions: 'creator' must be present and match sender.
exists $content->{creator} or
die "Expected 'creator' key in content for room version $room_version";
$content->{creator} eq $user->user_id or
die "Expected creator to be ${\$user->user_id}";
}

return 1;
});
Expand Down
Loading