identifiers: Make KeyId a DST

This commit is contained in:
Jonas Platte 2021-11-25 22:31:57 +01:00
parent 3ce578f384
commit 5852658da5
No known key found for this signature in database
GPG Key ID: 7D261D771D915378
14 changed files with 187 additions and 126 deletions

View File

@ -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<Box<ServerName>, BTreeMap<ServerSigningKeyId, String>>,
pub signatures: BTreeMap<Box<ServerName>, BTreeMap<Box<ServerSigningKeyId>, String>>,
}
impl<'a> ThirdPartySigned<'a> {
@ -44,7 +44,7 @@ impl<'a> ThirdPartySigned<'a> {
sender: &'a UserId,
mxid: &'a UserId,
token: &'a str,
signatures: BTreeMap<Box<ServerName>, BTreeMap<ServerSigningKeyId, String>>,
signatures: BTreeMap<Box<ServerName>, BTreeMap<Box<ServerSigningKeyId>, String>>,
) -> Self {
Self { sender, mxid, token, signatures }
}

View File

@ -89,7 +89,7 @@ pub struct RoomV1Pdu {
pub hashes: EventHash,
/// Signatures for the PDU.
pub signatures: BTreeMap<Box<ServerName>, BTreeMap<ServerSigningKeyId, String>>,
pub signatures: BTreeMap<Box<ServerName>, BTreeMap<Box<ServerSigningKeyId>, 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<Box<ServerName>, BTreeMap<ServerSigningKeyId, String>>,
pub signatures: BTreeMap<Box<ServerName>, BTreeMap<Box<ServerSigningKeyId>, String>>,
}
/// Content hashes of a PDU.

View File

@ -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<Box<ServerName>, BTreeMap<ServerSigningKeyId, String>>,
pub signatures: BTreeMap<Box<ServerName>, BTreeMap<Box<ServerSigningKeyId>, 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<UserId>,
signatures: BTreeMap<Box<ServerName>, BTreeMap<ServerSigningKeyId, String>>,
signatures: BTreeMap<Box<ServerName>, BTreeMap<Box<ServerSigningKeyId>, 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"

View File

@ -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);

View File

@ -54,15 +54,15 @@ pub struct ServerSigningKeys {
pub server_name: Box<ServerName>,
/// Public keys of the homeserver for verifying digital signatures.
pub verify_keys: BTreeMap<ServerSigningKeyId, VerifyKey>,
pub verify_keys: BTreeMap<Box<ServerSigningKeyId>, VerifyKey>,
/// Public keys that the homeserver used to use and when it stopped using them.
pub old_verify_keys: BTreeMap<ServerSigningKeyId, OldVerifyKey>,
pub old_verify_keys: BTreeMap<Box<ServerSigningKeyId>, OldVerifyKey>,
/// Digital signatures of this object signed using the verify_keys.
///
/// Map of server name to keys by key ID.
pub signatures: BTreeMap<Box<ServerName>, BTreeMap<ServerSigningKeyId, String>>,
pub signatures: BTreeMap<Box<ServerName>, BTreeMap<Box<ServerSigningKeyId>, String>>,
/// Timestamp when the keys should be refreshed.
///

View File

@ -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<Box<ServerName>, BTreeMap<ServerSigningKeyId, QueryCriteria>>,
pub server_keys: BTreeMap<Box<ServerName>, BTreeMap<Box<ServerSigningKeyId>, QueryCriteria>>,
}
@ -41,7 +41,7 @@ ruma_api! {
impl Request {
/// Creates a new `Request` with the given query criteria.
pub fn new(
server_keys: BTreeMap<Box<ServerName>, BTreeMap<ServerSigningKeyId, QueryCriteria>>,
server_keys: BTreeMap<Box<ServerName>, BTreeMap<Box<ServerSigningKeyId>, QueryCriteria>>,
) -> Self {
Self { server_keys }
}

View File

@ -77,7 +77,7 @@ pub struct ThirdPartyInvite {
pub sender: Box<UserId>,
/// Signature from the identity server using a long-term private key.
pub signed: BTreeMap<Box<ServerName>, BTreeMap<ServerSigningKeyId, String>>,
pub signed: BTreeMap<Box<ServerName>, BTreeMap<Box<ServerSigningKeyId>, String>>,
}
impl ThirdPartyInvite {
@ -87,7 +87,7 @@ impl ThirdPartyInvite {
mxid: Box<UserId>,
room_id: Box<RoomId>,
sender: Box<UserId>,
signed: BTreeMap<Box<ServerName>, BTreeMap<ServerSigningKeyId, String>>,
signed: BTreeMap<Box<ServerName>, BTreeMap<Box<ServerSigningKeyId>, String>>,
) -> Self {
Self { medium: Medium::Email, address, mxid, room_id, sender, signed }
}

View File

@ -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()

View File

@ -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<NonZeroU8, Error> {
/// Checks an identifier that contains a localpart and hostname for validity.
fn parse_id(id: &str, valid_sigils: &[char]) -> Result<usize, Error> {
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.

View File

@ -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

View File

@ -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<A, K: ?Sized> {
full_id: Box<str>,
colon_idx: NonZeroU8,
_phantom: PhantomData<(A, K)>,
}
/// A key algorithm and key name delimited by a colon.
#[repr(transparent)]
pub struct KeyId<A, K: ?Sized>(PhantomData<(A, K)>, str);
impl<A, K: ?Sized> KeyId<A, K> {
/// 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<Self>
where
A: AsRef<str>,
K: AsRef<str>,
@ -32,11 +28,7 @@ impl<A, K: ?Sized> KeyId<A, K> {
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<A, K: ?Sized> KeyId<A, K> {
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<A, K: ?Sized> KeyId<A, K> {
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<S, A, K: ?Sized>(key_identifier: S) -> Result<KeyId<A, K>, crate::Error>
where
S: AsRef<str> + Into<Box<str>>,
{
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<A, K: ?Sized> KeyId<A, K> {
/// Creates a string slice from this `KeyId<A, K>`
/// 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<A, K>`
/// 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<str>) -> Box<Self> {
unsafe { Box::from_raw(Box::into_raw(s) as _) }
}
fn into_owned(self: Box<Self>) -> Box<str> {
unsafe { Box::from_raw(Box::into_raw(self) as _) }
}
fn colon_idx(&self) -> usize {
self.as_str().find(':').unwrap()
}
}
impl<A, K: ?Sized> Clone for KeyId<A, K> {
/// Algorithm + key name for signing keys.
pub type SigningKeyId<K> = KeyId<SigningKeyAlgorithm, K>;
/// Algorithm + key name for homeserver signing keys.
pub type ServerSigningKeyId = SigningKeyId<KeyName>;
/// Algorithm + key name for device keys.
pub type DeviceSigningKeyId = SigningKeyId<DeviceId>;
impl<A, K: ?Sized> Clone for Box<KeyId<A, K>> {
fn clone(&self) -> Self {
Self { full_id: self.full_id.clone(), colon_idx: self.colon_idx, _phantom: PhantomData }
(**self).to_owned()
}
}
impl<A, K: ?Sized> ToOwned for KeyId<A, K> {
type Owned = Box<KeyId<A, K>>;
fn to_owned(&self) -> Self::Owned {
Self::from_owned(self.1.into())
}
}
impl<A, K: ?Sized> From<&KeyId<A, K>> for Box<KeyId<A, K>> {
fn from(id: &KeyId<A, K>) -> Self {
id.to_owned()
}
}
impl<A, K: ?Sized> AsRef<str> for Box<KeyId<A, K>> {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl<A, K: ?Sized> From<&KeyId<A, K>> for std::rc::Rc<KeyId<A, K>> {
fn from(s: &KeyId<A, K>) -> std::rc::Rc<KeyId<A, K>> {
let rc = std::rc::Rc::<str>::from(s.as_str());
unsafe { std::rc::Rc::from_raw(std::rc::Rc::into_raw(rc) as *const KeyId<A, K>) }
}
}
impl<A, K: ?Sized> From<&KeyId<A, K>> for std::sync::Arc<KeyId<A, K>> {
fn from(s: &KeyId<A, K>) -> std::sync::Arc<KeyId<A, K>> {
let arc = std::sync::Arc::<str>::from(s.as_str());
unsafe { std::sync::Arc::from_raw(std::sync::Arc::into_raw(arc) as *const KeyId<A, K>) }
}
}
impl<A, K: ?Sized> PartialEq<KeyId<A, K>> for Box<KeyId<A, K>> {
fn eq(&self, other: &KeyId<A, K>) -> bool {
self.as_str() == other.as_str()
}
}
impl<A, K: ?Sized> PartialEq<&'_ KeyId<A, K>> for Box<KeyId<A, K>> {
fn eq(&self, other: &&KeyId<A, K>) -> bool {
self.as_str() == other.as_str()
}
}
impl<A, K: ?Sized> PartialEq<Box<KeyId<A, K>>> for KeyId<A, K> {
fn eq(&self, other: &Box<KeyId<A, K>>) -> bool {
self.as_str() == other.as_str()
}
}
impl<A, K: ?Sized> PartialEq<Box<KeyId<A, K>>> for &'_ KeyId<A, K> {
fn eq(&self, other: &Box<KeyId<A, K>>) -> bool {
self.as_str() == other.as_str()
}
}
@ -89,54 +152,15 @@ impl<A, K: ?Sized> AsRef<str> for KeyId<A, K> {
}
}
impl<A, K: ?Sized> From<KeyId<A, K>> for String {
fn from(id: KeyId<A, K>) -> Self {
id.full_id.into()
}
}
impl<A, K: ?Sized> FromStr for KeyId<A, K> {
type Err = crate::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
try_from(s)
}
}
impl<A, K: ?Sized> TryFrom<&str> for KeyId<A, K> {
type Error = crate::Error;
fn try_from(s: &str) -> Result<Self, Self::Error> {
try_from(s)
}
}
impl<A, K: ?Sized> TryFrom<String> for KeyId<A, K>
where
A: FromStr,
K: From<String>,
{
type Error = crate::Error;
fn try_from(s: String) -> Result<Self, Self::Error> {
try_from(s)
impl<A, K: ?Sized> fmt::Display for KeyId<A, K> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_str().fmt(f)
}
}
impl<A, K: ?Sized> fmt::Debug for KeyId<A, K> {
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<A, K: ?Sized> fmt::Display for KeyId<A, K> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
self.as_str().fmt(f)
}
}
@ -150,13 +174,13 @@ impl<A, K: ?Sized> Eq for KeyId<A, K> {}
impl<A, K: ?Sized> PartialOrd for KeyId<A, K> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.as_str().partial_cmp(other.as_str())
PartialOrd::partial_cmp(self.as_str(), other.as_str())
}
}
impl<A, K: ?Sized> Ord for KeyId<A, K> {
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<A, K: ?Sized> Hash for KeyId<A, K> {
self.as_str().hash(state);
}
}
#[cfg(feature = "serde")]
impl<A, K: ?Sized> serde::Serialize for KeyId<A, K> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@ -176,24 +199,63 @@ impl<A, K: ?Sized> serde::Serialize for KeyId<A, K> {
}
}
impl<A, K: ?Sized> From<Box<KeyId<A, K>>> for String {
fn from(id: Box<KeyId<A, K>>) -> Self {
id.into_owned().into()
}
}
#[cfg(feature = "serde")]
impl<'de, A, K: ?Sized> serde::Deserialize<'de> for KeyId<A, K> {
impl<'de, A, K: ?Sized> serde::Deserialize<'de> for Box<KeyId<A, K>> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
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, A, K: ?Sized>(s: S) -> Result<Box<KeyId<A, K>>, crate::Error>
where
S: AsRef<str> + Into<Box<str>>,
{
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<A, K> {
type Error = crate::Error;
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
(ruma_identifiers_validation::key_id::validate)(s)?;
Ok(KeyId::from_borrowed(s))
}
}
impl<A, K: ?Sized> FromStr for Box<KeyId<A, K>> {
type Err = crate::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
try_from(s)
}
}
impl<A, K: ?Sized> TryFrom<&str> for Box<KeyId<A, K>> {
type Error = crate::Error;
fn try_from(s: &str) -> Result<Self, Self::Error> {
try_from(s)
}
}
impl<A, K: ?Sized> TryFrom<String> for Box<KeyId<A, K>> {
type Error = crate::Error;
fn try_from(s: String) -> Result<Self, Self::Error> {
try_from(s)
}
}
#[rustfmt::skip]
partial_eq_string!(KeyId<A, K> [A, K]);
/// Algorithm + key name for signing keys.
pub type SigningKeyId<K> = KeyId<SigningKeyAlgorithm, K>;
/// Algorithm + key name for homeserver signing keys.
pub type ServerSigningKeyId = SigningKeyId<KeyName>;
/// Algorithm + key name for device keys.
pub type DeviceSigningKeyId = SigningKeyId<DeviceId>;

View File

@ -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<K> = BTreeMap<SigningKeyId<K>, String>;
pub type EntitySignatures<K> = BTreeMap<Box<SigningKeyId<K>>, String>;
/// Map of all signatures, grouped by entity
///
@ -35,7 +35,7 @@ impl<E: Ord, K: ?Sized> Signatures<E, K> {
pub fn insert(
&mut self,
entity: E,
key_identifier: SigningKeyId<K>,
key_identifier: Box<SigningKeyId<K>>,
value: String,
) -> Option<String> {
self.0.entry(entity).or_insert_with(Default::default).insert(key_identifier, value)

View File

@ -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 }
}
}

View File

@ -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<T>` since they are DST's.