signatures: fix encoding of X-Matrix header
The values need to be escaped properly, just quoting them is not enough.
This commit is contained in:
parent
2df733997a
commit
230a90ef39
@ -5,6 +5,10 @@ Breaking changes:
|
|||||||
- The `XMatrix::new` method now takes `OwnedServerName` instead of `Option<OwnedServerName>`
|
- The `XMatrix::new` method now takes `OwnedServerName` instead of `Option<OwnedServerName>`
|
||||||
for the destination, since servers must always set the destination.
|
for the destination, since servers must always set the destination.
|
||||||
|
|
||||||
|
Improvements:
|
||||||
|
|
||||||
|
- When encoding to a header value, `XMatrix` fields are now quoted and escaped correctly.
|
||||||
|
|
||||||
# 0.3.0
|
# 0.3.0
|
||||||
|
|
||||||
Breaking changes:
|
Breaking changes:
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
//! Common types for implementing federation authorization.
|
//! Common types for implementing federation authorization.
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use headers::{authorization::Credentials, HeaderValue};
|
use headers::{authorization::Credentials, HeaderValue};
|
||||||
use ruma_common::{OwnedServerName, OwnedServerSigningKeyId};
|
use ruma_common::{OwnedServerName, OwnedServerSigningKeyId};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
@ -219,6 +221,15 @@ fn is_quoted_pair(c: u8) -> bool {
|
|||||||
c == b'\t' || c == b' ' || is_vchar(c) || is_obs_text(c)
|
c == b'\t' || c == b' ' || is_vchar(c) || is_obs_text(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn escape_value(value: &str) -> Cow<'_, str> {
|
||||||
|
if !value.is_empty() && value.chars().all(|c| u8::try_from(c).is_ok_and(is_tchar)) {
|
||||||
|
return Cow::Borrowed(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = value.replace('\\', r#"\\"#).replace('"', r#"\""#);
|
||||||
|
Cow::Owned(format!("\"{value}\""))
|
||||||
|
}
|
||||||
|
|
||||||
impl Credentials for XMatrix {
|
impl Credentials for XMatrix {
|
||||||
const SCHEME: &'static str = "X-Matrix";
|
const SCHEME: &'static str = "X-Matrix";
|
||||||
|
|
||||||
@ -228,13 +239,15 @@ impl Credentials for XMatrix {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn encode(&self) -> HeaderValue {
|
fn encode(&self) -> HeaderValue {
|
||||||
|
let origin = escape_value(self.origin.as_str());
|
||||||
|
let key = escape_value(self.key.as_str());
|
||||||
|
let sig = escape_value(&self.sig);
|
||||||
|
|
||||||
if let Some(destination) = &self.destination {
|
if let Some(destination) = &self.destination {
|
||||||
format!(
|
let destination = escape_value(destination.as_str());
|
||||||
"X-Matrix origin=\"{}\",destination=\"{destination}\",key=\"{}\",sig=\"{}\"",
|
format!("X-Matrix origin={origin},destination={destination},key={key},sig={sig}")
|
||||||
self.origin, self.key, self.sig
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
format!("X-Matrix origin=\"{}\",key=\"{}\",sig=\"{}\"", self.origin, self.key, self.sig)
|
format!("X-Matrix origin={origin},key={key},sig={sig}")
|
||||||
}
|
}
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("header format is static")
|
.expect("header format is static")
|
||||||
@ -264,7 +277,10 @@ mod tests {
|
|||||||
|
|
||||||
let credentials = XMatrix { origin, destination: None, key, sig };
|
let credentials = XMatrix { origin, destination: None, key, sig };
|
||||||
|
|
||||||
assert_eq!(credentials.encode(), header);
|
assert_eq!(
|
||||||
|
credentials.encode(),
|
||||||
|
"X-Matrix origin=origin.hs.example.com,key=\"ed25519:key1\",sig=ABCDEF..."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -282,6 +298,29 @@ mod tests {
|
|||||||
|
|
||||||
let credentials = XMatrix::new(origin, destination, key, sig);
|
let credentials = XMatrix::new(origin, destination, key, sig);
|
||||||
|
|
||||||
assert_eq!(credentials.encode(), header);
|
assert_eq!(credentials.encode(), "X-Matrix origin=origin.hs.example.com,destination=destination.hs.example.com,key=\"ed25519:key1\",sig=ABCDEF...");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn xmatrix_quoting() {
|
||||||
|
let header = HeaderValue::from_static(
|
||||||
|
r#"X-Matrix origin=example.com:1234,key="ed25519:key1",sig="abc\"def\\","#,
|
||||||
|
);
|
||||||
|
|
||||||
|
let origin: OwnedServerName = "example.com:1234".try_into().unwrap();
|
||||||
|
let key = "ed25519:key1".try_into().unwrap();
|
||||||
|
let sig = r#"abc"def\"#.to_owned();
|
||||||
|
let credentials: XMatrix = Credentials::decode(&header).unwrap();
|
||||||
|
assert_eq!(credentials.origin, origin);
|
||||||
|
assert_eq!(credentials.destination, None);
|
||||||
|
assert_eq!(credentials.key, key);
|
||||||
|
assert_eq!(credentials.sig, sig);
|
||||||
|
|
||||||
|
let credentials = XMatrix { origin, destination: None, key, sig };
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
credentials.encode(),
|
||||||
|
r#"X-Matrix origin="example.com:1234",key="ed25519:key1",sig="abc\"def\\""#
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user