fix(keys): only use keys valid at the time of PDU or transaction, and actually refresh keys

Previously, we only fetched keys once, only requesting them again if we have any missing, allowing for ancient keys to be used to sign PDUs and transactions
Now we refresh keys that either have or are about to expire, preventing attacks that make use of leaked private keys of a homeserver
We also ensure that when validating PDUs or transactions, that they are valid at the origin_server_ts or time of us receiving the transaction respectfully
As to not break event authorization for old rooms, we need to keep old keys around
We move verify_keys which we no longer see in direct requests to the origin to old_verify_keys
We keep old_verify_keys indefinitely as mentioned above, as to not break event authorization (at least until a future MSC addresses this)
This commit is contained in:
Matthias Ahouansou 2024-06-12 19:22:19 +02:00 committed by Timo Kösters
parent 144d548ef7
commit c453d45598
No known key found for this signature in database
GPG key ID: 0B25E636FBA7E4CB
8 changed files with 584 additions and 236 deletions

View file

@ -14,7 +14,7 @@ use http::{Request, StatusCode};
use ruma::{
api::{client::error::ErrorKind, AuthScheme, IncomingRequest, OutgoingResponse},
server_util::authorization::XMatrix,
CanonicalJsonValue, OwnedDeviceId, OwnedUserId, UserId,
CanonicalJsonValue, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedUserId, UserId,
};
use serde::Deserialize;
use tracing::{debug, error, warn};
@ -231,7 +231,7 @@ where
let keys_result = services()
.rooms
.event_handler
.fetch_signing_keys(&x_matrix.origin, vec![x_matrix.key.to_string()])
.fetch_signing_keys(&x_matrix.origin, vec![x_matrix.key.to_string()], false)
.await;
let keys = match keys_result {
@ -245,8 +245,19 @@ where
}
};
let pub_key_map =
BTreeMap::from_iter([(x_matrix.origin.as_str().to_owned(), keys)]);
// Only verify_keys that are currently valid should be used for validating requests
// as per MSC4029
let pub_key_map = BTreeMap::from_iter([(
x_matrix.origin.as_str().to_owned(),
if keys.valid_until_ts > MilliSecondsSinceUnixEpoch::now() {
keys.verify_keys
.into_iter()
.map(|(id, key)| (id, key.key))
.collect()
} else {
BTreeMap::new()
},
)]);
match ruma::signatures::verify_json(&pub_key_map, &request_map) {
Ok(()) => (None, None, Some(x_matrix.origin), None),