identifiers: Make DeviceKeyId a DST

This commit is contained in:
Jonas Platte 2021-09-17 20:22:45 +02:00
parent 206c25e7e7
commit c73eb7dce3
No known key found for this signature in database
GPG Key ID: 7D261D771D915378
8 changed files with 49 additions and 59 deletions

View File

@ -48,7 +48,7 @@ pub enum BackupAlgorithm {
public_key: String, public_key: String,
/// Signatures of the auth_data as Signed JSON. /// Signatures of the auth_data as Signed JSON.
signatures: BTreeMap<UserId, BTreeMap<DeviceKeyId, String>>, signatures: BTreeMap<UserId, BTreeMap<Box<DeviceKeyId>, String>>,
}, },
} }

View File

@ -60,4 +60,4 @@ impl Response {
} }
/// The one-time keys for a given device. /// The one-time keys for a given device.
pub type OneTimeKeys = BTreeMap<Box<DeviceId>, BTreeMap<DeviceKeyId, OneTimeKey>>; pub type OneTimeKeys = BTreeMap<Box<DeviceId>, BTreeMap<Box<DeviceKeyId>, OneTimeKey>>;

View File

@ -27,7 +27,7 @@ ruma_api! {
/// One-time public keys for "pre-key" messages. /// One-time public keys for "pre-key" messages.
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub one_time_keys: Option<BTreeMap<DeviceKeyId, OneTimeKey>>, pub one_time_keys: Option<BTreeMap<Box<DeviceKeyId>, OneTimeKey>>,
} }
response: { response: {

View File

@ -25,10 +25,10 @@ pub struct DeviceKeys {
pub algorithms: Vec<EventEncryptionAlgorithm>, pub algorithms: Vec<EventEncryptionAlgorithm>,
/// Public identity keys. /// Public identity keys.
pub keys: BTreeMap<DeviceKeyId, String>, pub keys: BTreeMap<Box<DeviceKeyId>, String>,
/// Signatures for the device key object. /// Signatures for the device key object.
pub signatures: BTreeMap<UserId, BTreeMap<DeviceKeyId, String>>, pub signatures: BTreeMap<UserId, BTreeMap<Box<DeviceKeyId>, String>>,
/// Additional data added to the device key information by intermediate servers, and /// Additional data added to the device key information by intermediate servers, and
/// not covered by the signatures. /// not covered by the signatures.
@ -43,8 +43,8 @@ impl DeviceKeys {
user_id: UserId, user_id: UserId,
device_id: Box<DeviceId>, device_id: Box<DeviceId>,
algorithms: Vec<EventEncryptionAlgorithm>, algorithms: Vec<EventEncryptionAlgorithm>,
keys: BTreeMap<DeviceKeyId, String>, keys: BTreeMap<Box<DeviceKeyId>, String>,
signatures: BTreeMap<UserId, BTreeMap<DeviceKeyId, String>>, signatures: BTreeMap<UserId, BTreeMap<Box<DeviceKeyId>, String>>,
) -> Self { ) -> Self {
Self { user_id, device_id, algorithms, keys, signatures, unsigned: Default::default() } Self { user_id, device_id, algorithms, keys, signatures, unsigned: Default::default() }
} }
@ -72,7 +72,7 @@ impl UnsignedDeviceInfo {
} }
/// Signatures for a `SignedKey` object. /// Signatures for a `SignedKey` object.
pub type SignedKeySignatures = BTreeMap<UserId, BTreeMap<DeviceKeyId, String>>; pub type SignedKeySignatures = BTreeMap<UserId, BTreeMap<Box<DeviceKeyId>, String>>;
/// A key for the SignedCurve25519 algorithm /// A key for the SignedCurve25519 algorithm
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]

View File

@ -47,7 +47,8 @@ impl Response {
pub type OneTimeKeyClaims = BTreeMap<UserId, BTreeMap<Box<DeviceId>, DeviceKeyAlgorithm>>; pub type OneTimeKeyClaims = BTreeMap<UserId, BTreeMap<Box<DeviceId>, DeviceKeyAlgorithm>>;
/// One time keys for use in pre-key messages /// One time keys for use in pre-key messages
pub type OneTimeKeys = BTreeMap<UserId, BTreeMap<Box<DeviceId>, BTreeMap<DeviceKeyId, OneTimeKey>>>; pub type OneTimeKeys =
BTreeMap<UserId, BTreeMap<Box<DeviceId>, BTreeMap<Box<DeviceKeyId>, OneTimeKey>>>;
/// A key and its signature /// A key and its signature
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@ -57,12 +58,15 @@ pub struct KeyObject {
pub key: String, pub key: String,
/// Signature of the key object. /// Signature of the key object.
pub signatures: BTreeMap<UserId, BTreeMap<DeviceKeyId, String>>, pub signatures: BTreeMap<UserId, BTreeMap<Box<DeviceKeyId>, String>>,
} }
impl KeyObject { impl KeyObject {
/// Creates a new `KeyObject` with the given key and signatures. /// Creates a new `KeyObject` with the given key and signatures.
pub fn new(key: String, signatures: BTreeMap<UserId, BTreeMap<DeviceKeyId, String>>) -> Self { pub fn new(
key: String,
signatures: BTreeMap<UserId, BTreeMap<Box<DeviceKeyId>, String>>,
) -> Self {
Self { key, signatures } Self { key, signatures }
} }
} }

View File

@ -31,7 +31,7 @@ pub fn device_key_id(input: TokenStream) -> TokenStream {
assert!(device_key_id::validate(&id.value()).is_ok(), "Invalid device key id"); assert!(device_key_id::validate(&id.value()).is_ok(), "Invalid device key id");
let output = quote! { let output = quote! {
<#dollar_crate::DeviceKeyId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap() <&#dollar_crate::DeviceKeyId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap()
}; };
output.into() output.into()

View File

@ -1,10 +1,12 @@
use std::num::NonZeroU8;
use crate::Error; use crate::Error;
pub fn validate(s: &str) -> Result<NonZeroU8, Error> { pub fn validate(s: &str) -> Result<(), Error> {
let colon_idx = NonZeroU8::new(s.find(':').ok_or(Error::MissingDelimiter)? as u8) let colon_idx = s.find(':').ok_or(Error::MissingDelimiter)?;
.ok_or(Error::InvalidKeyAlgorithm)?;
Ok(colon_idx) if colon_idx == 0 {
Err(Error::InvalidKeyAlgorithm)
} else {
// Any non-empty string is accepted as a key algorithm for forwards compatibility
Ok(())
}
} }

View File

@ -1,25 +1,17 @@
//! Identifiers for device keys for end-to-end encryption. //! Identifiers for device keys for end-to-end encryption.
use std::{convert::TryInto, fmt, num::NonZeroU8}; use ruma_identifiers_validation::device_key_id::validate;
use crate::{crypto_algorithms::DeviceKeyAlgorithm, DeviceId}; use crate::{crypto_algorithms::DeviceKeyAlgorithm, DeviceId};
opaque_identifier_validated! {
/// A key algorithm and a device id, combined with a ':'. /// A key algorithm and a device id, combined with a ':'.
#[derive(Clone)] pub type DeviceKeyId [ validate ];
pub struct DeviceKeyId {
full_id: Box<str>,
colon_idx: NonZeroU8,
}
impl fmt::Debug for DeviceKeyId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.full_id.fmt(f)
}
} }
impl DeviceKeyId { impl DeviceKeyId {
/// Create a `DeviceKeyId` from a `DeviceKeyAlgorithm` and a `DeviceId`. /// Create a `DeviceKeyId` from a `DeviceKeyAlgorithm` and a `DeviceId`.
pub fn from_parts(algorithm: DeviceKeyAlgorithm, device_id: &DeviceId) -> Self { pub fn from_parts(algorithm: DeviceKeyAlgorithm, device_id: &DeviceId) -> Box<Self> {
let algorithm: &str = algorithm.as_ref(); let algorithm: &str = algorithm.as_ref();
let device_id: &str = device_id.as_ref(); let device_id: &str = device_id.as_ref();
@ -28,33 +20,23 @@ impl DeviceKeyId {
res.push(':'); res.push(':');
res.push_str(device_id); res.push_str(device_id);
let colon_idx = Self::from_owned(res.into())
NonZeroU8::new(algorithm.len().try_into().expect("no algorithm name len > 255"))
.expect("no empty algorithm name");
DeviceKeyId { full_id: res.into(), colon_idx }
} }
/// Returns key algorithm of the device key ID. /// Returns key algorithm of the device key ID.
pub fn algorithm(&self) -> DeviceKeyAlgorithm { pub fn algorithm(&self) -> DeviceKeyAlgorithm {
self.full_id[..self.colon_idx.get() as usize].into() self.as_str()[..self.colon_idx()].into()
} }
/// Returns device ID of the device key ID. /// Returns device ID of the device key ID.
pub fn device_id(&self) -> &DeviceId { pub fn device_id(&self) -> &DeviceId {
(&self.full_id[self.colon_idx.get() as usize + 1..]).into() self.as_str()[self.colon_idx() + 1..].into()
}
} }
fn try_from<S>(key_id: S) -> Result<DeviceKeyId, crate::Error> fn colon_idx(&self) -> usize {
where self.as_str().find(':').unwrap()
S: AsRef<str> + Into<Box<str>>, }
{
let colon_idx = ruma_identifiers_validation::device_key_id::validate(key_id.as_ref())?;
Ok(DeviceKeyId { full_id: key_id.into(), colon_idx })
} }
common_impls!(DeviceKeyId, try_from, "Device key ID with algorithm and device ID");
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -66,9 +48,8 @@ mod tests {
#[test] #[test]
fn convert_device_key_id() { fn convert_device_key_id() {
assert_eq!( assert_eq!(
DeviceKeyId::try_from("ed25519:JLAFKJWSCS") <&DeviceKeyId>::try_from("ed25519:JLAFKJWSCS")
.expect("Failed to create device key ID.") .expect("Failed to create device key ID."),
.as_ref(),
"ed25519:JLAFKJWSCS" "ed25519:JLAFKJWSCS"
); );
} }
@ -76,7 +57,7 @@ mod tests {
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
#[test] #[test]
fn serialize_device_key_id() { fn serialize_device_key_id() {
let device_key_id = DeviceKeyId::try_from("ed25519:JLAFKJWSCS").unwrap(); let device_key_id = <&DeviceKeyId>::try_from("ed25519:JLAFKJWSCS").unwrap();
let serialized = serde_json::to_value(device_key_id).unwrap(); let serialized = serde_json::to_value(device_key_id).unwrap();
assert_eq!(serialized, serde_json::json!("ed25519:JLAFKJWSCS")); assert_eq!(serialized, serde_json::json!("ed25519:JLAFKJWSCS"));
@ -85,40 +66,43 @@ mod tests {
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
#[test] #[test]
fn deserialize_device_key_id() { fn deserialize_device_key_id() {
let deserialized: DeviceKeyId = let deserialized: Box<DeviceKeyId> =
serde_json::from_value(serde_json::json!("ed25519:JLAFKJWSCS")).unwrap(); serde_json::from_value(serde_json::json!("ed25519:JLAFKJWSCS")).unwrap();
let expected = DeviceKeyId::try_from("ed25519:JLAFKJWSCS").unwrap(); let expected = <&DeviceKeyId>::try_from("ed25519:JLAFKJWSCS").unwrap();
assert_eq!(deserialized, expected); assert_eq!(deserialized, expected);
} }
#[test] #[test]
fn missing_key_algorithm() { fn missing_key_algorithm() {
assert_eq!(DeviceKeyId::try_from(":JLAFKJWSCS").unwrap_err(), Error::InvalidKeyAlgorithm); assert_eq!(
<&DeviceKeyId>::try_from(":JLAFKJWSCS").unwrap_err(),
Error::InvalidKeyAlgorithm
);
} }
#[test] #[test]
fn missing_delimiter() { fn missing_delimiter() {
assert_eq!( assert_eq!(
DeviceKeyId::try_from("ed25519|JLAFKJWSCS").unwrap_err(), <&DeviceKeyId>::try_from("ed25519|JLAFKJWSCS").unwrap_err(),
Error::MissingDelimiter, Error::MissingDelimiter,
); );
} }
#[test] #[test]
fn empty_device_id_ok() { fn empty_device_id_ok() {
assert!(DeviceKeyId::try_from("ed25519:").is_ok()); assert!(<&DeviceKeyId>::try_from("ed25519:").is_ok());
} }
#[test] #[test]
fn valid_key_algorithm() { fn valid_key_algorithm() {
let device_key_id = DeviceKeyId::try_from("ed25519:JLAFKJWSCS").unwrap(); let device_key_id = <&DeviceKeyId>::try_from("ed25519:JLAFKJWSCS").unwrap();
assert_eq!(device_key_id.algorithm(), DeviceKeyAlgorithm::Ed25519); assert_eq!(device_key_id.algorithm(), DeviceKeyAlgorithm::Ed25519);
} }
#[test] #[test]
fn valid_device_id() { fn valid_device_id() {
let device_key_id = DeviceKeyId::try_from("ed25519:JLAFKJWSCS").unwrap(); let device_key_id = <&DeviceKeyId>::try_from("ed25519:JLAFKJWSCS").unwrap();
assert_eq!(device_key_id.device_id(), "JLAFKJWSCS"); assert_eq!(device_key_id.device_id(), "JLAFKJWSCS");
} }
} }