identifiers: Allow to validate KeyId with any key name type

Use new trait bounds for KeyId to only allow specific types.
Change the KeyId validation to be generic over the key name type.
Remove the KeyName type and replace it with the more specific
ServerSigningKeyVersion.
This commit is contained in:
Kévin Commaille 2024-10-08 15:48:03 +02:00 committed by strawberry
parent f47733b49d
commit ae3897cad8
21 changed files with 196 additions and 98 deletions

View File

@ -15,6 +15,16 @@ Breaking changes:
with `serde_html_form`.
- The `header` attribute for the `request` and `response` macros accepts any
type that implements `ToString` and `FromStr`.
- The `compat-key-id` cargo feature was renamed to
`compat-server-signing-key-version`.
- `(Owned)KeyName` was renamed to `(Owned)ServerSigningKeyVersion` and is now
validated according to the set of allowed characters defined in the docs,
unless the `compat-server-signing-key-version` cargo feature is enabled.
- The bounds on `KeyId` changed. The algorithm part must implement
`KeyAlgorithm` and the key name part must implement `KeyName`.
- The `(owned_)server_signing_key_id` macros were removed. For compile-time
validated construction, use `ServerSigningKeyId::from_parts` with a
`SigningKeyAlgorithm` and the `server_signing_key_version` macro.
Improvements:

View File

@ -38,8 +38,8 @@ compat-arbitrary-length-ids = [
"ruma-identifiers-validation/compat-arbitrary-length-ids",
]
# Don't validate the version part in `KeyId`.
compat-key-id = ["ruma-identifiers-validation/compat-key-id"]
# Don't validate `ServerSigningKeyVersion`.
compat-server-signing-key-version = ["ruma-identifiers-validation/compat-server-signing-key-version"]
# Allow some user IDs that are invalid even with the specified historical
# user ID scheme.

View File

@ -5,9 +5,12 @@
#![allow(unused_qualifications)]
#[doc(inline)]
pub use ruma_identifiers_validation::error::{
pub use ruma_identifiers_validation::{
error::{
Error as IdParseError, MatrixIdError, MatrixToError, MatrixUriError, MxcUriError,
VoipVersionIdError,
},
KeyName,
};
use serde::de::{self, Deserializer, Unexpected};
@ -21,10 +24,9 @@ pub use self::{
device_key_id::{DeviceKeyId, OwnedDeviceKeyId},
event_id::{EventId, OwnedEventId},
key_id::{
DeviceSigningKeyId, KeyId, OwnedDeviceSigningKeyId, OwnedKeyId, OwnedServerSigningKeyId,
OwnedSigningKeyId, ServerSigningKeyId, SigningKeyId,
DeviceSigningKeyId, KeyAlgorithm, KeyId, OwnedDeviceSigningKeyId, OwnedKeyId,
OwnedServerSigningKeyId, OwnedSigningKeyId, ServerSigningKeyId, SigningKeyId,
},
key_name::{KeyName, OwnedKeyName},
matrix_uri::{MatrixToUri, MatrixUri},
mxc_uri::{Mxc, MxcUri, OwnedMxcUri},
room_alias_id::{OwnedRoomAliasId, RoomAliasId},
@ -32,6 +34,7 @@ pub use self::{
room_or_alias_id::{OwnedRoomOrAliasId, RoomOrAliasId},
room_version_id::RoomVersionId,
server_name::{OwnedServerName, ServerName},
server_signing_key_version::{OwnedServerSigningKeyVersion, ServerSigningKeyVersion},
session_id::{OwnedSessionId, SessionId},
signatures::{DeviceSignatures, EntitySignatures, ServerSignatures, Signatures},
transaction_id::{OwnedTransactionId, TransactionId},
@ -49,13 +52,13 @@ mod device_id;
mod device_key_id;
mod event_id;
mod key_id;
mod key_name;
mod mxc_uri;
mod room_alias_id;
mod room_id;
mod room_or_alias_id;
mod room_version_id;
mod server_name;
mod server_signing_key_version;
mod session_id;
mod signatures;
mod transaction_id;
@ -107,7 +110,7 @@ macro_rules! owned_device_id {
pub mod __private_macros {
pub use ruma_macros::{
device_key_id, event_id, mxc_uri, room_alias_id, room_id, room_version_id, server_name,
server_signing_key_id, user_id,
server_signing_key_version, user_id,
};
}
@ -183,19 +186,19 @@ macro_rules! room_version_id {
};
}
/// Compile-time checked [`ServerSigningKeyId`] construction.
/// Compile-time checked [`ServerSigningKeyVersion`] construction.
#[macro_export]
macro_rules! server_signing_key_id {
macro_rules! server_signing_key_version {
($s:literal) => {
$crate::__private_macros::server_signing_key_id!($crate, $s)
$crate::__private_macros::server_signing_key_version!($crate, $s)
};
}
/// Compile-time checked [`OwnedServerSigningKeyId`] construction.
/// Compile-time checked [`OwnedServerSigningKeyVersion`] construction.
#[macro_export]
macro_rules! owned_server_signing_key_id {
macro_rules! owned_server_signing_key_version {
($s:literal) => {
$crate::server_signing_key_id!($s).to_owned()
$crate::server_signing_key_version!($s).to_owned()
};
}

View File

@ -2,8 +2,9 @@ use ruma_macros::IdZst;
#[cfg(feature = "rand")]
use super::generate_localpart;
use super::{IdParseError, KeyName};
/// A Matrix key ID.
/// A Matrix device ID.
///
/// Device identifiers in Matrix are completely opaque character sequences. This type is provided
/// simply for its semantic value.
@ -40,6 +41,18 @@ impl DeviceId {
}
}
impl KeyName for DeviceId {
fn validate(_s: &str) -> Result<(), IdParseError> {
Ok(())
}
}
impl KeyName for OwnedDeviceId {
fn validate(_s: &str) -> Result<(), IdParseError> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::{DeviceId, OwnedDeviceId};

View File

@ -2,26 +2,23 @@ use std::{
cmp::Ordering,
hash::{Hash, Hasher},
marker::PhantomData,
str::FromStr,
};
use ruma_macros::IdZst;
use super::{crypto_algorithms::SigningKeyAlgorithm, DeviceId, KeyName};
use super::{crypto_algorithms::SigningKeyAlgorithm, DeviceId, KeyName, ServerSigningKeyVersion};
/// A key algorithm and key name delimited by a colon.
#[repr(transparent)]
#[derive(IdZst)]
#[ruma_id(validate = ruma_identifiers_validation::key_id::validate)]
pub struct KeyId<A, K: ?Sized>(PhantomData<(A, K)>, str);
#[ruma_id(
validate = ruma_identifiers_validation::key_id::validate::<K>,
)]
pub struct KeyId<A: KeyAlgorithm, K: KeyName + ?Sized>(PhantomData<(A, K)>, str);
impl<A, K: ?Sized> KeyId<A, K> {
impl<A: KeyAlgorithm, K: KeyName + ?Sized> KeyId<A, K> {
/// Creates a new `KeyId` from an algorithm and key name.
pub fn from_parts(algorithm: A, key_name: &K) -> OwnedKeyId<A, K>
where
A: AsRef<str>,
K: AsRef<str>,
{
pub fn from_parts(algorithm: A, key_name: &K) -> OwnedKeyId<A, K> {
let algorithm = algorithm.as_ref();
let key_name = key_name.as_ref();
@ -34,19 +31,16 @@ impl<A, K: ?Sized> KeyId<A, K> {
}
/// Returns key algorithm of the key ID.
pub fn algorithm(&self) -> A
where
A: FromStr,
{
A::from_str(&self.as_str()[..self.colon_idx()]).unwrap_or_else(|_| unreachable!())
pub fn algorithm(&self) -> A {
A::from(&self.as_str()[..self.colon_idx()])
}
/// Returns the key name of the key ID.
pub fn key_name<'a>(&'a self) -> &'a K
where
&'a K: From<&'a str>,
&'a K: TryFrom<&'a str>,
{
self.as_str()[self.colon_idx() + 1..].into()
<&'a K>::try_from(&self.as_str()[..self.colon_idx()]).unwrap_or_else(|_| unreachable!())
}
fn colon_idx(&self) -> usize {
@ -61,10 +55,10 @@ pub type SigningKeyId<K> = KeyId<SigningKeyAlgorithm, K>;
pub type OwnedSigningKeyId<K> = OwnedKeyId<SigningKeyAlgorithm, K>;
/// Algorithm + key name for homeserver signing keys.
pub type ServerSigningKeyId = SigningKeyId<KeyName>;
pub type ServerSigningKeyId = SigningKeyId<ServerSigningKeyVersion>;
/// Algorithm + key name for homeserver signing keys.
pub type OwnedServerSigningKeyId = OwnedSigningKeyId<KeyName>;
pub type OwnedServerSigningKeyId = OwnedSigningKeyId<ServerSigningKeyVersion>;
/// Algorithm + key name for device keys.
pub type DeviceSigningKeyId = SigningKeyId<DeviceId>;
@ -74,28 +68,33 @@ pub type OwnedDeviceSigningKeyId = OwnedSigningKeyId<DeviceId>;
// The following impls are usually derived using the std macros.
// They are implemented manually here to avoid unnecessary bounds.
impl<A, K: ?Sized> PartialEq for KeyId<A, K> {
impl<A: KeyAlgorithm, K: KeyName + ?Sized> PartialEq for KeyId<A, K> {
fn eq(&self, other: &Self) -> bool {
self.as_str() == other.as_str()
}
}
impl<A, K: ?Sized> Eq for KeyId<A, K> {}
impl<A: KeyAlgorithm, K: KeyName + ?Sized> Eq for KeyId<A, K> {}
impl<A, K: ?Sized> PartialOrd for KeyId<A, K> {
impl<A: KeyAlgorithm, K: KeyName + ?Sized> PartialOrd for KeyId<A, K> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<A, K: ?Sized> Ord for KeyId<A, K> {
impl<A: KeyAlgorithm, K: KeyName + ?Sized> Ord for KeyId<A, K> {
fn cmp(&self, other: &Self) -> Ordering {
Ord::cmp(self.as_str(), other.as_str())
}
}
impl<A, K: ?Sized> Hash for KeyId<A, K> {
impl<A: KeyAlgorithm, K: KeyName + ?Sized> Hash for KeyId<A, K> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_str().hash(state);
}
}
/// The algorithm of a key.
pub trait KeyAlgorithm: for<'a> From<&'a str> + AsRef<str> {}
impl KeyAlgorithm for SigningKeyAlgorithm {}

View File

@ -1,9 +0,0 @@
use ruma_macros::IdZst;
/// A Matrix key identifier.
///
/// Key identifiers in Matrix are opaque character sequences of `[a-zA-Z_]`. This type is
/// provided simply for its semantic value.
#[repr(transparent)]
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
pub struct KeyName(str);

View File

@ -0,0 +1,31 @@
use ruma_macros::IdZst;
use super::{IdParseError, KeyName};
/// The version of a [homeserver signing key].
///
/// This is an opaque character sequences of `[a-zA-Z0-9_]`. This type is provided simply for its
/// semantic value.
///
/// With the `compat-server-signing-key-version` cargo feature, the validation of this type is
/// relaxed to accept any string.
///
/// [homeserver signing key]: https://spec.matrix.org/latest/server-server-api/#retrieving-server-keys
#[repr(transparent)]
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
#[ruma_id(
validate = ruma_identifiers_validation::server_signing_key_version::validate,
)]
pub struct ServerSigningKeyVersion(str);
impl KeyName for ServerSigningKeyVersion {
fn validate(s: &str) -> Result<(), IdParseError> {
ruma_identifiers_validation::server_signing_key_version::validate(s)
}
}
impl KeyName for OwnedServerSigningKeyVersion {
fn validate(s: &str) -> Result<(), IdParseError> {
ruma_identifiers_validation::server_signing_key_version::validate(s)
}
}

View File

@ -2,7 +2,10 @@ use std::{borrow::Borrow, collections::BTreeMap};
use serde::{Deserialize, Serialize};
use super::{OwnedDeviceId, OwnedKeyName, OwnedServerName, OwnedSigningKeyId, OwnedUserId};
use super::{
KeyName, OwnedDeviceId, OwnedServerName, OwnedServerSigningKeyVersion, OwnedSigningKeyId,
OwnedUserId,
};
/// Map of key identifier to signature values.
pub type EntitySignatures<K> = BTreeMap<OwnedSigningKeyId<K>, String>;
@ -10,8 +13,11 @@ pub type EntitySignatures<K> = BTreeMap<OwnedSigningKeyId<K>, String>;
/// Map of all signatures, grouped by entity
///
/// ```
/// # use ruma_common::{server_name, KeyId, Signatures, SigningKeyAlgorithm};
/// let key_identifier = KeyId::from_parts(SigningKeyAlgorithm::Ed25519, "1");
/// # use ruma_common::{server_name, server_signing_key_version, ServerSigningKeyId, Signatures, SigningKeyAlgorithm};
/// let key_identifier = ServerSigningKeyId::from_parts(
/// SigningKeyAlgorithm::Ed25519,
/// server_signing_key_version!("1")
/// );
/// let mut signatures = Signatures::new();
/// let server_name = server_name!("example.org");
/// let signature =
@ -20,9 +26,9 @@ pub type EntitySignatures<K> = BTreeMap<OwnedSigningKeyId<K>, String>;
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Signatures<E: Ord, K: ?Sized>(BTreeMap<E, EntitySignatures<K>>);
pub struct Signatures<E: Ord, K: KeyName + ?Sized>(BTreeMap<E, EntitySignatures<K>>);
impl<E: Ord, K: ?Sized> Signatures<E, K> {
impl<E: Ord, K: KeyName + ?Sized> Signatures<E, K> {
/// Creates an empty signature map.
pub fn new() -> Self {
Self(BTreeMap::new())
@ -51,7 +57,7 @@ impl<E: Ord, K: ?Sized> Signatures<E, K> {
}
/// Map of server signatures for an event, grouped by server.
pub type ServerSignatures = Signatures<OwnedServerName, OwnedKeyName>;
pub type ServerSignatures = Signatures<OwnedServerName, OwnedServerSigningKeyVersion>;
/// Map of device signatures for an event, grouped by user.
pub type DeviceSignatures = Signatures<OwnedUserId, OwnedDeviceId>;

View File

@ -7,7 +7,7 @@ fn main() {
_ = ruma_common::room_id!("!1234567890:matrix.org");
_ = ruma_common::room_version_id!("1");
_ = ruma_common::room_version_id!("1-custom");
_ = ruma_common::server_signing_key_id!("ed25519:Abc_1");
_ = ruma_common::server_signing_key_version!("Abc_1");
_ = ruma_common::server_name!("myserver.fish");
_ = ruma_common::user_id!("@user:ruma.io");

View File

@ -581,8 +581,8 @@ mod tests {
use js_int::uint;
use maplit::btreemap;
use ruma_common::{
mxc_uri, owned_server_signing_key_id, serde::CanBeEmpty, server_name, user_id,
MilliSecondsSinceUnixEpoch,
mxc_uri, serde::CanBeEmpty, server_name, server_signing_key_version, user_id,
MilliSecondsSinceUnixEpoch, ServerSigningKeyId, SigningKeyAlgorithm,
};
use serde_json::{from_value as from_json_value, json};
@ -710,7 +710,10 @@ mod tests {
third_party_invite.signed.signatures,
btreemap! {
server_name!("magic.forest").to_owned() => btreemap! {
owned_server_signing_key_id!("ed25519:3") => "foobar".to_owned()
ServerSigningKeyId::from_parts(
SigningKeyAlgorithm::Ed25519,
server_signing_key_version!("3")
) => "foobar".to_owned()
}
}
);
@ -780,7 +783,10 @@ mod tests {
third_party_invite.signed.signatures,
btreemap! {
server_name!("magic.forest").to_owned() => btreemap! {
owned_server_signing_key_id!("ed25519:3") => "foobar".to_owned()
ServerSigningKeyId::from_parts(
SigningKeyAlgorithm::Ed25519,
server_signing_key_version!("3")
) => "foobar".to_owned()
}
}
);

View File

@ -4,8 +4,9 @@ use std::collections::BTreeMap;
use js_int::uint;
use ruma_common::{
event_id, owned_event_id, owned_room_id, owned_server_signing_key_id, owned_user_id,
server_name, MilliSecondsSinceUnixEpoch,
event_id, owned_event_id, owned_room_id, owned_user_id, server_name,
server_signing_key_version, MilliSecondsSinceUnixEpoch, ServerSigningKeyId,
SigningKeyAlgorithm,
};
use ruma_events::{
pdu::{EventHash, Pdu, RoomV1Pdu, RoomV3Pdu},
@ -21,7 +22,10 @@ fn serialize_pdu_as_v1() {
let mut signatures = BTreeMap::new();
let mut inner_signature = BTreeMap::new();
inner_signature.insert(
owned_server_signing_key_id!("ed25519:key_version"),
ServerSigningKeyId::from_parts(
SigningKeyAlgorithm::Ed25519,
server_signing_key_version!("key_version"),
),
"86BytesOfSignatureOfTheRedactedEvent".into(),
);
signatures.insert(server_name!("example.com").to_owned(), inner_signature);
@ -86,7 +90,10 @@ fn serialize_pdu_as_v3() {
let mut signatures = BTreeMap::new();
let mut inner_signature = BTreeMap::new();
inner_signature.insert(
owned_server_signing_key_id!("ed25519:key_version"),
ServerSigningKeyId::from_parts(
SigningKeyAlgorithm::Ed25519,
server_signing_key_version!("key_version"),
),
"86BytesOfSignatureOfTheRedactedEvent".into(),
);
signatures.insert(server_name!("example.com").to_owned(), inner_signature);

View File

@ -1,5 +1,17 @@
# [unreleased]
Breaking changes:
- `key_id::validate` takes a generic parameter that implements the new `KeyName`
trait to validate the key name part. This allows to validate key names that
are not only server signing key versions.
- The `compat-key-id` cargo feature was renamed to
`compat-server-signing-key-version`.
Improvements:
- Add `server_signing_key_version::validate`.
# 0.9.5
Bug fixes:

View File

@ -15,8 +15,8 @@ all-features = true
# Allow IDs to exceed 255 bytes.
compat-arbitrary-length-ids = []
# Don't validate the version part in `key_id::validate`.
compat-key-id = []
# Don't validate the version in `server_signing_key_version::validate`.
compat-server-signing-key-version = []
# Allow some user IDs that are invalid even with the specified historical
# user ID scheme.

View File

@ -1,24 +1,12 @@
use std::num::NonZeroU8;
use crate::Error;
use crate::{Error, KeyName};
pub fn validate(s: &str) -> Result<NonZeroU8, Error> {
pub fn validate<K: KeyName + ?Sized>(s: &str) -> Result<NonZeroU8, Error> {
let colon_idx =
NonZeroU8::new(s.find(':').ok_or(Error::MissingColon)? as u8).ok_or(Error::MissingColon)?;
#[cfg(not(feature = "compat-key-id"))]
validate_version(&s[colon_idx.get() as usize + 1..])?;
K::validate(&s[colon_idx.get() as usize + 1..])?;
Ok(colon_idx)
}
#[cfg(not(feature = "compat-key-id"))]
fn validate_version(version: &str) -> Result<(), Error> {
if version.is_empty() {
return Err(Error::Empty);
} else if !version.chars().all(|c| c.is_alphanumeric() || c == '_') {
return Err(Error::InvalidCharacters);
}
Ok(())
}

View File

@ -12,6 +12,7 @@ pub mod room_id;
pub mod room_id_or_alias_id;
pub mod room_version_id;
pub mod server_name;
pub mod server_signing_key_version;
pub mod user_id;
pub mod voip_version_id;
@ -48,3 +49,9 @@ fn validate_delimited_id(id: &str, first_byte: u8) -> Result<(), Error> {
parse_id(id, first_byte)?;
Ok(())
}
/// Helper trait to validate the name of a key.
pub trait KeyName: AsRef<str> {
/// Validate the given string for this name.
fn validate(s: &str) -> Result<(), Error>;
}

View File

@ -0,0 +1,15 @@
use crate::Error;
#[cfg_attr(feature = "compat-server-signing-key-version", allow(unused_variables))]
pub fn validate(s: &str) -> Result<(), Error> {
#[cfg(not(feature = "compat-server-signing-key-version"))]
{
if s.is_empty() {
return Err(Error::Empty);
} else if !s.chars().all(|c| c.is_alphanumeric() || c == '_') {
return Err(Error::InvalidCharacters);
}
}
Ok(())
}

View File

@ -15,8 +15,8 @@ use proc_macro::TokenStream;
use proc_macro2 as pm2;
use quote::quote;
use ruma_identifiers_validation::{
device_key_id, event_id, key_id, mxc_uri, room_alias_id, room_id, room_version_id, server_name,
user_id,
device_key_id, event_id, mxc_uri, room_alias_id, room_id, room_version_id, server_name,
server_signing_key_version, user_id,
};
use syn::{parse_macro_input, DeriveInput, ItemEnum, ItemStruct};
@ -211,14 +211,17 @@ pub fn room_version_id(input: TokenStream) -> TokenStream {
output.into()
}
/// Compile-time checked `ServerSigningKeyId` construction.
/// Compile-time checked `ServerSigningKeyVersion` construction.
#[proc_macro]
pub fn server_signing_key_id(input: TokenStream) -> TokenStream {
pub fn server_signing_key_version(input: TokenStream) -> TokenStream {
let IdentifierInput { dollar_crate, id } = parse_macro_input!(input as IdentifierInput);
assert!(key_id::validate(&id.value()).is_ok(), "Invalid server_signing_key_id");
assert!(
server_signing_key_version::validate(&id.value()).is_ok(),
"Invalid server_signing_key_version"
);
let output = quote! {
<&#dollar_crate::ServerSigningKeyId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap()
<&#dollar_crate::ServerSigningKeyVersion as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap()
};
output.into()

View File

@ -1003,7 +1003,7 @@ mod tests {
let encoded_public_key = Base64::new(newly_generated_key_pair.public_key().to_vec());
let version = ServerSigningKeyId::from_parts(
SigningKeyAlgorithm::Ed25519,
key_pair_sender.version().into(),
key_pair_sender.version().try_into().unwrap(),
);
sender_key_map.insert(version.to_string(), encoded_public_key);
public_key_map.insert("domain-sender".to_owned(), sender_key_map);
@ -1177,8 +1177,10 @@ mod tests {
fn add_key_to_map(public_key_map: &mut PublicKeyMap, name: &str, pair: &Ed25519KeyPair) {
let sender_key_map = public_key_map.entry(name.to_owned()).or_default();
let encoded_public_key = Base64::new(pair.public_key().to_vec());
let version =
ServerSigningKeyId::from_parts(SigningKeyAlgorithm::Ed25519, pair.version().into());
let version = ServerSigningKeyId::from_parts(
SigningKeyAlgorithm::Ed25519,
pair.version().try_into().unwrap(),
);
sender_key_map.insert(version.to_string(), encoded_public_key);
}
@ -1192,7 +1194,7 @@ mod tests {
let encoded_public_key = Base64::new(pair.public_key().to_vec());
let version = ServerSigningKeyId::from_parts(
SigningKeyAlgorithm::from("an-unknown-algorithm"),
pair.version().into(),
pair.version().try_into().unwrap(),
);
sender_key_map.insert(version.to_string(), encoded_public_key);

View File

@ -8,8 +8,10 @@ static PKCS8_ED25519_DER: &[u8] = include_bytes!("./keys/ed25519.der");
fn add_key_to_map(public_key_map: &mut PublicKeyMap, name: &str, pair: &Ed25519KeyPair) {
let sender_key_map = public_key_map.entry(name.to_owned()).or_default();
let encoded_public_key = Base64::new(pair.public_key().to_vec());
let version =
ServerSigningKeyId::from_parts(SigningKeyAlgorithm::Ed25519, pair.version().into());
let version = ServerSigningKeyId::from_parts(
SigningKeyAlgorithm::Ed25519,
pair.version().try_into().unwrap(),
);
sender_key_map.insert(version.to_string(), encoded_public_key);
}

View File

@ -1,5 +1,8 @@
# [unreleased]
- The `compat-key-id` cargo feature was renamed to
`compat-server-signing-key-version`.
# 0.10.1
Upgrade `ruma-events` to 0.28.1.

View File

@ -133,7 +133,7 @@ full = [
# Enable all compatibility hacks. Deprecated.
compat = [
"compat-key-id",
"compat-server-signing-key-version",
"compat-user-id",
"compat-empty-string-null",
"compat-null",
@ -147,8 +147,8 @@ compat = [
# Allow IDs to exceed 255 bytes.
compat-arbitrary-length-ids = ["ruma-common/compat-arbitrary-length-ids"]
# Don't validate the version part in `KeyId`.
compat-key-id = ["ruma-common/compat-key-id"]
# Don't validate `ServerSigningKeyVersion`.
compat-server-signing-key-version = ["ruma-common/compat-server-signing-key-version"]
# Allow some user IDs that are invalid even with the specified historical
# user ID scheme.