diff --git a/crates/ruma-client-api/src/r0/membership.rs b/crates/ruma-client-api/src/r0/membership.rs index 8e2ddd66..b1c94de5 100644 --- a/crates/ruma-client-api/src/r0/membership.rs +++ b/crates/ruma-client-api/src/r0/membership.rs @@ -34,7 +34,7 @@ pub struct ThirdPartySigned<'a> { pub token: &'a str, /// A signatures object containing a signature of the entire signed object. - pub signatures: BTreeMap, BTreeMap>, + pub signatures: BTreeMap, BTreeMap, String>>, } impl<'a> ThirdPartySigned<'a> { @@ -44,7 +44,7 @@ impl<'a> ThirdPartySigned<'a> { sender: &'a UserId, mxid: &'a UserId, token: &'a str, - signatures: BTreeMap, BTreeMap>, + signatures: BTreeMap, BTreeMap, String>>, ) -> Self { Self { sender, mxid, token, signatures } } diff --git a/crates/ruma-events/src/pdu.rs b/crates/ruma-events/src/pdu.rs index 7b8d335f..b9bc739b 100644 --- a/crates/ruma-events/src/pdu.rs +++ b/crates/ruma-events/src/pdu.rs @@ -89,7 +89,7 @@ pub struct RoomV1Pdu { pub hashes: EventHash, /// Signatures for the PDU. - pub signatures: BTreeMap, BTreeMap>, + pub signatures: BTreeMap, BTreeMap, String>>, } /// A 'persistent data unit' (event) for room versions 3 and beyond. @@ -146,7 +146,7 @@ pub struct RoomV3Pdu { pub hashes: EventHash, /// Signatures for the PDU. - pub signatures: BTreeMap, BTreeMap>, + pub signatures: BTreeMap, BTreeMap, String>>, } /// Content hashes of a PDU. diff --git a/crates/ruma-events/src/room/member.rs b/crates/ruma-events/src/room/member.rs index 059f605f..bbc37290 100644 --- a/crates/ruma-events/src/room/member.rs +++ b/crates/ruma-events/src/room/member.rs @@ -178,7 +178,7 @@ pub struct SignedContent { /// A single signature from the verifying server, in the format specified by the Signing Events /// section of the server-server API. - pub signatures: BTreeMap, BTreeMap>, + pub signatures: BTreeMap, BTreeMap, String>>, /// The token property of the containing `third_party_invite` object. pub token: String, @@ -188,7 +188,7 @@ impl SignedContent { /// Creates a new `SignedContent` with the given mxid, signature and token. pub fn new( mxid: Box, - signatures: BTreeMap, BTreeMap>, + signatures: BTreeMap, BTreeMap, String>>, token: String, ) -> Self { Self { mxid, signatures, token } @@ -495,7 +495,7 @@ mod tests { && mxid == "@alice:example.org" && signatures == btreemap! { server_name!("magic.forest") => btreemap! { - server_signing_key_id!("ed25519:3") => "foobar".to_owned() + server_signing_key_id!("ed25519:3").to_owned() => "foobar".to_owned() } } && token == "abc123" @@ -580,7 +580,7 @@ mod tests { && mxid == "@alice:example.org" && signatures == btreemap! { server_name!("magic.forest") => btreemap! { - server_signing_key_id!("ed25519:3") => "foobar".to_owned() + server_signing_key_id!("ed25519:3").to_owned() => "foobar".to_owned() } } && token == "abc123" @@ -654,7 +654,7 @@ mod tests { && mxid == "@alice:example.org" && signatures == btreemap! { server_name!("magic.forest") => btreemap! { - server_signing_key_id!("ed25519:3") => "foobar".to_owned() + server_signing_key_id!("ed25519:3").to_owned() => "foobar".to_owned() } } && token == "abc123" diff --git a/crates/ruma-events/tests/pdu.rs b/crates/ruma-events/tests/pdu.rs index 58b460ae..5edd9a86 100644 --- a/crates/ruma-events/tests/pdu.rs +++ b/crates/ruma-events/tests/pdu.rs @@ -18,7 +18,7 @@ fn serialize_pdu_as_v1() { let mut signatures = BTreeMap::new(); let mut inner_signature = BTreeMap::new(); inner_signature.insert( - server_signing_key_id!("ed25519:key_version"), + server_signing_key_id!("ed25519:key_version").to_owned(), "86BytesOfSignatureOfTheRedactedEvent".into(), ); signatures.insert(server_name!("example.com"), inner_signature); @@ -85,7 +85,7 @@ fn serialize_pdu_as_v3() { let mut signatures = BTreeMap::new(); let mut inner_signature = BTreeMap::new(); inner_signature.insert( - server_signing_key_id!("ed25519:key_version"), + server_signing_key_id!("ed25519:key_version").to_owned(), "86BytesOfSignatureOfTheRedactedEvent".into(), ); signatures.insert(server_name!("example.com"), inner_signature); diff --git a/crates/ruma-federation-api/src/discovery.rs b/crates/ruma-federation-api/src/discovery.rs index b4558ac6..d0e1cdd3 100644 --- a/crates/ruma-federation-api/src/discovery.rs +++ b/crates/ruma-federation-api/src/discovery.rs @@ -54,15 +54,15 @@ pub struct ServerSigningKeys { pub server_name: Box, /// Public keys of the homeserver for verifying digital signatures. - pub verify_keys: BTreeMap, + pub verify_keys: BTreeMap, VerifyKey>, /// Public keys that the homeserver used to use and when it stopped using them. - pub old_verify_keys: BTreeMap, + pub old_verify_keys: BTreeMap, OldVerifyKey>, /// Digital signatures of this object signed using the verify_keys. /// /// Map of server name to keys by key ID. - pub signatures: BTreeMap, BTreeMap>, + pub signatures: BTreeMap, BTreeMap, String>>, /// Timestamp when the keys should be refreshed. /// diff --git a/crates/ruma-federation-api/src/discovery/get_remote_server_keys_batch/v2.rs b/crates/ruma-federation-api/src/discovery/get_remote_server_keys_batch/v2.rs index d64f221d..70846454 100644 --- a/crates/ruma-federation-api/src/discovery/get_remote_server_keys_batch/v2.rs +++ b/crates/ruma-federation-api/src/discovery/get_remote_server_keys_batch/v2.rs @@ -28,7 +28,7 @@ ruma_api! { /// notary server must return an empty server_keys array in the response. /// /// The notary server may return multiple keys regardless of the Key IDs given. - pub server_keys: BTreeMap, BTreeMap>, + pub server_keys: BTreeMap, BTreeMap, QueryCriteria>>, } @@ -41,7 +41,7 @@ ruma_api! { impl Request { /// Creates a new `Request` with the given query criteria. pub fn new( - server_keys: BTreeMap, BTreeMap>, + server_keys: BTreeMap, BTreeMap, QueryCriteria>>, ) -> Self { Self { server_keys } } diff --git a/crates/ruma-federation-api/src/thirdparty/bind_callback/v1.rs b/crates/ruma-federation-api/src/thirdparty/bind_callback/v1.rs index 26201a06..200e7024 100644 --- a/crates/ruma-federation-api/src/thirdparty/bind_callback/v1.rs +++ b/crates/ruma-federation-api/src/thirdparty/bind_callback/v1.rs @@ -77,7 +77,7 @@ pub struct ThirdPartyInvite { pub sender: Box, /// Signature from the identity server using a long-term private key. - pub signed: BTreeMap, BTreeMap>, + pub signed: BTreeMap, BTreeMap, String>>, } impl ThirdPartyInvite { @@ -87,7 +87,7 @@ impl ThirdPartyInvite { mxid: Box, room_id: Box, sender: Box, - signed: BTreeMap, BTreeMap>, + signed: BTreeMap, BTreeMap, String>>, ) -> Self { Self { medium: Medium::Email, address, mxid, room_id, sender, signed } } diff --git a/crates/ruma-identifiers-macros/src/lib.rs b/crates/ruma-identifiers-macros/src/lib.rs index a7dacb5c..ea071905 100644 --- a/crates/ruma-identifiers-macros/src/lib.rs +++ b/crates/ruma-identifiers-macros/src/lib.rs @@ -91,7 +91,7 @@ pub fn server_signing_key_id(input: TokenStream) -> TokenStream { assert!(key_id::validate(&id.value()).is_ok(), "Invalid server_signing_key_id"); let output = quote! { - <#dollar_crate::ServerSigningKeyId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap() + <&#dollar_crate::ServerSigningKeyId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap() }; output.into() diff --git a/crates/ruma-identifiers-validation/src/lib.rs b/crates/ruma-identifiers-validation/src/lib.rs index 33898e0d..abec1fea 100644 --- a/crates/ruma-identifiers-validation/src/lib.rs +++ b/crates/ruma-identifiers-validation/src/lib.rs @@ -16,8 +16,6 @@ pub mod server_name; pub mod session_id; pub mod user_id; -use std::num::NonZeroU8; - pub use error::Error; /// All identifiers must be 255 bytes or less. @@ -36,13 +34,12 @@ fn validate_id(id: &str, valid_sigils: &[char]) -> Result<(), Error> { Ok(()) } -/// Checks an identifier that contains a localpart and hostname for validity, -/// and returns the index of the colon that separates the two. -fn parse_id(id: &str, valid_sigils: &[char]) -> Result { +/// Checks an identifier that contains a localpart and hostname for validity. +fn parse_id(id: &str, valid_sigils: &[char]) -> Result { validate_id(id, valid_sigils)?; let colon_idx = id.find(':').ok_or(Error::MissingDelimiter)?; server_name::validate(&id[colon_idx + 1..])?; - Ok(NonZeroU8::new(colon_idx as u8).unwrap()) + Ok(colon_idx) } /// Checks an identifier that contains a localpart and hostname for validity. diff --git a/crates/ruma-identifiers-validation/src/user_id.rs b/crates/ruma-identifiers-validation/src/user_id.rs index 51e2f4e1..9880a36d 100644 --- a/crates/ruma-identifiers-validation/src/user_id.rs +++ b/crates/ruma-identifiers-validation/src/user_id.rs @@ -1,13 +1,11 @@ -use std::num::NonZeroU8; - use crate::{parse_id, Error}; -pub fn validate(s: &str) -> Result<(NonZeroU8, bool), Error> { +pub fn validate(s: &str) -> Result<(), Error> { let colon_idx = parse_id(s, &['@'])?; - let localpart = &s[1..colon_idx.get() as usize]; - let is_historical = localpart_is_fully_conforming(localpart)?; + let localpart = &s[1..colon_idx]; + let _ = localpart_is_fully_conforming(localpart)?; - Ok((colon_idx, is_historical)) + Ok(()) } /// Check whether the given user id localpart is valid and fully conforming diff --git a/crates/ruma-identifiers/src/key_id.rs b/crates/ruma-identifiers/src/key_id.rs index b7d66df0..bb5a3488 100644 --- a/crates/ruma-identifiers/src/key_id.rs +++ b/crates/ruma-identifiers/src/key_id.rs @@ -1,25 +1,21 @@ use std::{ cmp::Ordering, - convert::{TryFrom, TryInto}, + convert::TryFrom, fmt, hash::{Hash, Hasher}, marker::PhantomData, - num::NonZeroU8, str::FromStr, }; use crate::{crypto_algorithms::SigningKeyAlgorithm, DeviceId, KeyName}; -/// A key algorithm and key name delimited by a colon -pub struct KeyId { - full_id: Box, - colon_idx: NonZeroU8, - _phantom: PhantomData<(A, K)>, -} +/// A key algorithm and key name delimited by a colon. +#[repr(transparent)] +pub struct KeyId(PhantomData<(A, K)>, str); impl KeyId { /// Creates a new `KeyId` from an algorithm and key name. - pub fn from_parts(algorithm: A, key_name: &K) -> Self + pub fn from_parts(algorithm: A, key_name: &K) -> Box where A: AsRef, K: AsRef, @@ -32,11 +28,7 @@ impl KeyId { res.push(':'); res.push_str(key_name); - let colon_idx = - NonZeroU8::new(algorithm.len().try_into().expect("no algorithm name len > 255")) - .expect("no empty algorithm name"); - - KeyId { full_id: res.into(), colon_idx, _phantom: PhantomData } + Self::from_owned(res.into()) } /// Returns key algorithm of the key ID. @@ -44,8 +36,7 @@ impl KeyId { where A: FromStr, { - A::from_str(&self.full_id[..self.colon_idx.get() as usize]) - .unwrap_or_else(|_| unreachable!()) + A::from_str(&self.as_str()[..self.colon_idx()]).unwrap_or_else(|_| unreachable!()) } /// Returns the key name of the key ID. @@ -53,33 +44,105 @@ impl KeyId { where &'a K: From<&'a str>, { - self.full_id[self.colon_idx.get() as usize + 1..].into() + self.as_str()[self.colon_idx() + 1..].into() } -} -fn try_from(key_identifier: S) -> Result, crate::Error> -where - S: AsRef + Into>, -{ - let colon_idx = ruma_identifiers_validation::key_id::validate(key_identifier.as_ref())?; - Ok(KeyId { full_id: key_identifier.into(), colon_idx, _phantom: PhantomData }) -} - -impl KeyId { - /// Creates a string slice from this `KeyId` + /// Creates a string slice from this `KeyId`. pub fn as_str(&self) -> &str { - &self.full_id + &self.1 } - /// Creates a byte slice from this `KeyId` + /// Creates a byte slice from this `KeyId`. pub fn as_bytes(&self) -> &[u8] { - self.full_id.as_bytes() + self.1.as_bytes() + } + + fn from_borrowed(s: &str) -> &Self { + unsafe { std::mem::transmute(s) } + } + + fn from_owned(s: Box) -> Box { + unsafe { Box::from_raw(Box::into_raw(s) as _) } + } + + fn into_owned(self: Box) -> Box { + unsafe { Box::from_raw(Box::into_raw(self) as _) } + } + + fn colon_idx(&self) -> usize { + self.as_str().find(':').unwrap() } } -impl Clone for KeyId { +/// Algorithm + key name for signing keys. +pub type SigningKeyId = KeyId; + +/// Algorithm + key name for homeserver signing keys. +pub type ServerSigningKeyId = SigningKeyId; + +/// Algorithm + key name for device keys. +pub type DeviceSigningKeyId = SigningKeyId; + +impl Clone for Box> { fn clone(&self) -> Self { - Self { full_id: self.full_id.clone(), colon_idx: self.colon_idx, _phantom: PhantomData } + (**self).to_owned() + } +} + +impl ToOwned for KeyId { + type Owned = Box>; + fn to_owned(&self) -> Self::Owned { + Self::from_owned(self.1.into()) + } +} + +impl From<&KeyId> for Box> { + fn from(id: &KeyId) -> Self { + id.to_owned() + } +} + +impl AsRef for Box> { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl From<&KeyId> for std::rc::Rc> { + fn from(s: &KeyId) -> std::rc::Rc> { + let rc = std::rc::Rc::::from(s.as_str()); + unsafe { std::rc::Rc::from_raw(std::rc::Rc::into_raw(rc) as *const KeyId) } + } +} + +impl From<&KeyId> for std::sync::Arc> { + fn from(s: &KeyId) -> std::sync::Arc> { + let arc = std::sync::Arc::::from(s.as_str()); + unsafe { std::sync::Arc::from_raw(std::sync::Arc::into_raw(arc) as *const KeyId) } + } +} + +impl PartialEq> for Box> { + fn eq(&self, other: &KeyId) -> bool { + self.as_str() == other.as_str() + } +} + +impl PartialEq<&'_ KeyId> for Box> { + fn eq(&self, other: &&KeyId) -> bool { + self.as_str() == other.as_str() + } +} + +impl PartialEq>> for KeyId { + fn eq(&self, other: &Box>) -> bool { + self.as_str() == other.as_str() + } +} + +impl PartialEq>> for &'_ KeyId { + fn eq(&self, other: &Box>) -> bool { + self.as_str() == other.as_str() } } @@ -89,54 +152,15 @@ impl AsRef for KeyId { } } -impl From> for String { - fn from(id: KeyId) -> Self { - id.full_id.into() - } -} - -impl FromStr for KeyId { - type Err = crate::Error; - - fn from_str(s: &str) -> Result { - try_from(s) - } -} - -impl TryFrom<&str> for KeyId { - type Error = crate::Error; - - fn try_from(s: &str) -> Result { - try_from(s) - } -} - -impl TryFrom for KeyId -where - A: FromStr, - K: From, -{ - type Error = crate::Error; - - fn try_from(s: String) -> Result { - try_from(s) +impl fmt::Display for KeyId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_str().fmt(f) } } impl fmt::Debug for KeyId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Using struct debug format for consistency with other ID types. - // FIXME: Change all ID types to have just a string debug format? - f.debug_struct("KeyId") - .field("full_id", &self.full_id) - .field("colon_idxs", &self.colon_idx) - .finish() - } -} - -impl fmt::Display for KeyId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.as_str()) + self.as_str().fmt(f) } } @@ -150,13 +174,13 @@ impl Eq for KeyId {} impl PartialOrd for KeyId { fn partial_cmp(&self, other: &Self) -> Option { - self.as_str().partial_cmp(other.as_str()) + PartialOrd::partial_cmp(self.as_str(), other.as_str()) } } impl Ord for KeyId { fn cmp(&self, other: &Self) -> Ordering { - self.as_str().cmp(other.as_str()) + Ord::cmp(self.as_str(), other.as_str()) } } @@ -165,7 +189,6 @@ impl Hash for KeyId { self.as_str().hash(state); } } - #[cfg(feature = "serde")] impl serde::Serialize for KeyId { fn serialize(&self, serializer: S) -> Result @@ -176,24 +199,63 @@ impl serde::Serialize for KeyId { } } +impl From>> for String { + fn from(id: Box>) -> Self { + id.into_owned().into() + } +} + #[cfg(feature = "serde")] -impl<'de, A, K: ?Sized> serde::Deserialize<'de> for KeyId { +impl<'de, A, K: ?Sized> serde::Deserialize<'de> for Box> { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { - crate::deserialize_id(deserializer, "Key name with algorithm and key identifier") + use serde::de::Error; + let s = String::deserialize(deserializer)?; + match try_from(s) { + Ok(o) => Ok(o), + Err(e) => Err(D::Error::custom(e)), + } + } +} + +fn try_from(s: S) -> Result>, crate::Error> +where + S: AsRef + Into>, +{ + ruma_identifiers_validation::key_id::validate(s.as_ref())?; + Ok(KeyId::from_owned(s.into())) +} + +impl<'a, A, K: ?Sized> TryFrom<&'a str> for &'a KeyId { + type Error = crate::Error; + fn try_from(s: &'a str) -> Result { + (ruma_identifiers_validation::key_id::validate)(s)?; + Ok(KeyId::from_borrowed(s)) + } +} + +impl FromStr for Box> { + type Err = crate::Error; + fn from_str(s: &str) -> Result { + try_from(s) + } +} + +impl TryFrom<&str> for Box> { + type Error = crate::Error; + fn try_from(s: &str) -> Result { + try_from(s) + } +} + +impl TryFrom for Box> { + type Error = crate::Error; + fn try_from(s: String) -> Result { + try_from(s) } } #[rustfmt::skip] partial_eq_string!(KeyId [A, K]); - -/// Algorithm + key name for signing keys. -pub type SigningKeyId = KeyId; - -/// Algorithm + key name for homeserver signing keys. -pub type ServerSigningKeyId = SigningKeyId; - -/// Algorithm + key name for device keys. -pub type DeviceSigningKeyId = SigningKeyId; diff --git a/crates/ruma-identifiers/src/signatures.rs b/crates/ruma-identifiers/src/signatures.rs index 85e31f37..b88f7dc3 100644 --- a/crates/ruma-identifiers/src/signatures.rs +++ b/crates/ruma-identifiers/src/signatures.rs @@ -3,7 +3,7 @@ use std::{borrow::Borrow, collections::BTreeMap}; use crate::{DeviceId, KeyName, ServerName, SigningKeyId, UserId}; /// Map of key identifier to signature values. -pub type EntitySignatures = BTreeMap, String>; +pub type EntitySignatures = BTreeMap>, String>; /// Map of all signatures, grouped by entity /// @@ -35,7 +35,7 @@ impl Signatures { pub fn insert( &mut self, entity: E, - key_identifier: SigningKeyId, + key_identifier: Box>, value: String, ) -> Option { self.0.entry(entity).or_insert_with(Default::default).insert(key_identifier, value) diff --git a/crates/ruma-identity-service-api/src/keys/get_public_key/v2.rs b/crates/ruma-identity-service-api/src/keys/get_public_key/v2.rs index 9f8036a8..51f0d05c 100644 --- a/crates/ruma-identity-service-api/src/keys/get_public_key/v2.rs +++ b/crates/ruma-identity-service-api/src/keys/get_public_key/v2.rs @@ -16,7 +16,7 @@ ruma_api! { request: { /// The ID of the key. #[ruma_api(path)] - pub key_id: ServerSigningKeyId, + pub key_id: &'a ServerSigningKeyId, } response: { @@ -25,9 +25,9 @@ ruma_api! { } } -impl Request { +impl<'a> Request<'a> { /// Create a `Request` with the given key_id. - pub fn new(key_id: ServerSigningKeyId) -> Self { + pub fn new(key_id: &'a ServerSigningKeyId) -> Self { Self { key_id } } } diff --git a/crates/ruma-serde-macros/src/outgoing.rs b/crates/ruma-serde-macros/src/outgoing.rs index d44fca39..30db0300 100644 --- a/crates/ruma-serde-macros/src/outgoing.rs +++ b/crates/ruma-serde-macros/src/outgoing.rs @@ -262,7 +262,9 @@ fn strip_lifetimes(field_type: &mut Type) -> bool { } else if last_seg.ident == "ClientSecret" || last_seg.ident == "DeviceId" || last_seg.ident == "DeviceKeyId" + || last_seg.ident == "DeviceSigningKeyId" || last_seg.ident == "EventId" + || last_seg.ident == "KeyId" || last_seg.ident == "MxcUri" || last_seg.ident == "ServerName" || last_seg.ident == "SessionId" @@ -271,6 +273,8 @@ fn strip_lifetimes(field_type: &mut Type) -> bool { || last_seg.ident == "RoomId" || last_seg.ident == "RoomIdOrAliasId" || last_seg.ident == "RoomName" + || last_seg.ident == "ServerSigningKeyId" + || last_seg.ident == "SigningKeyId" || last_seg.ident == "UserId" { // The identifiers that need to be boxed `Box` since they are DST's.