signatures: Rewrite redaction to be more efficient

This commit is contained in:
Jonas Platte 2020-09-17 21:36:29 +02:00
parent 414161f0fd
commit fa58f09a23
No known key found for this signature in database
GPG Key ID: CC154DE0E30B7C67

View File

@ -1,6 +1,6 @@
//! Functions for signing and verifying JSON and events. //! Functions for signing and verifying JSON and events.
use std::collections::HashMap; use std::{cmp, collections::HashMap, mem};
use base64::{decode_config, encode_config, STANDARD_NO_PAD}; use base64::{decode_config, encode_config, STANDARD_NO_PAD};
use ring::digest::{digest, SHA256}; use ring::digest::{digest, SHA256};
@ -33,9 +33,12 @@ static ALLOWED_KEYS: &[&str] = &[
"membership", "membership",
]; ];
/// The fields of an *m.room.power_levels* event's `content` key that are allowed to remain in an fn allowed_content_keys_for(event_type: &str) -> &'static [&'static str] {
/// event during redaction. match event_type {
static ALLOWED_POWER_LEVELS_KEYS: &[&str] = &[ "m.room.member" => &["membership"],
"m.room.create" => &["creator"],
"m.room.join_rules" => &["join_rule"],
"m.room.power_levels" => &[
"ban", "ban",
"events", "events",
"events_default", "events_default",
@ -44,7 +47,11 @@ static ALLOWED_POWER_LEVELS_KEYS: &[&str] = &[
"state_default", "state_default",
"users", "users",
"users_default", "users_default",
]; ],
"m.room.history_visibility" => &["history_visibility"],
_ => &[],
}
}
/// The fields to remove from a JSON object when converting JSON into the "canonical" form. /// The fields to remove from a JSON object when converting JSON into the "canonical" form.
static CANONICAL_JSON_FIELDS_TO_REMOVE: &[&str] = &["signatures", "unsigned"]; static CANONICAL_JSON_FIELDS_TO_REMOVE: &[&str] = &["signatures", "unsigned"];
@ -701,59 +708,33 @@ pub fn redact(value: &Value) -> Result<Value, Error> {
None => return Err(Error::new("field `type` in JSON value must be present")), None => return Err(Error::new("field `type` in JSON value must be present")),
}; };
let event_type = match event_type_value.as_str() { let allowed_content_keys = match event_type_value.as_str() {
Some(event_type) => event_type.to_string(), Some(event_type) => allowed_content_keys_for(event_type),
None => return Err(Error::new("field `type` in JSON value must be a JSON string")), None => return Err(Error::new("field `type` in JSON value must be a JSON string")),
}; };
if let Some(content_value) = event.get_mut("content") { if let Some(content_value) = event.get_mut("content") {
let map = match content_value { let content = match content_value {
Value::Object(ref mut map) => map, Value::Object(ref mut map) => map,
_ => return Err(Error::new("field `content` in JSON value must be a JSON object")), _ => return Err(Error::new("field `content` in JSON value must be a JSON object")),
}; };
for key in map.clone().keys() { let max_values = cmp::max(content.len(), allowed_content_keys.len());
match event_type.as_ref() { let mut old_content = mem::replace(content, serde_json::Map::with_capacity(max_values));
"m.room.member" => {
if key != "membership" { for &key in allowed_content_keys {
map.remove(key); if let Some(value) = old_content.remove(key) {
content.insert(key.to_owned(), value);
} }
} }
"m.room.create" => {
if key != "creator" {
map.remove(key);
}
}
"m.room.join_rules" => {
if key != "join_rules" {
map.remove(key);
}
}
"m.room.power_levels" => {
if !ALLOWED_POWER_LEVELS_KEYS.contains(&key.as_ref()) {
map.remove(key);
}
}
"m.room.aliases" => {
if key != "aliases" {
map.remove(key);
}
}
"m.room.history_visibility" => {
if key != "history_visibility" {
map.remove(key);
}
}
_ => {
map.remove(key);
}
};
}
} }
for key in event.clone().keys() { let max_values = cmp::max(event.len(), ALLOWED_KEYS.len());
if !ALLOWED_KEYS.contains(&key.as_ref()) { let mut old_event = mem::replace(event, serde_json::Map::with_capacity(max_values));
event.remove(key);
for &key in ALLOWED_KEYS {
if let Some(value) = old_event.remove(key) {
event.insert(key.to_owned(), value);
} }
} }