signatures: Fix verify_json and sign_json enforcing PDU size limits
These functions are used for request signatures too.
This commit is contained in:
parent
b7d0970335
commit
3c76fa1492
@ -7,21 +7,6 @@ pub mod value;
|
||||
|
||||
use value::Object as CanonicalJsonObject;
|
||||
|
||||
/// Returns a canonical JSON string according to Matrix specification.
|
||||
///
|
||||
/// This function should be preferred over `serde_json::to_string` since it checks the size of the
|
||||
/// canonical string. Matrix canonical JSON enforces a size limit of less than 65,535 when sending
|
||||
/// PDU's for the server-server protocol.
|
||||
pub fn to_string<T: Serialize>(val: &T) -> Result<String, Error> {
|
||||
let s = serde_json::to_string(val).map_err(Error::SerDe)?;
|
||||
|
||||
if s.as_bytes().len() > 65_535 {
|
||||
Err(Error::JsonSize)
|
||||
} else {
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
/// The set of possible errors when serializing to canonical JSON.
|
||||
#[derive(Debug)]
|
||||
#[allow(clippy::exhaustive_enums)]
|
||||
@ -29,9 +14,6 @@ pub enum Error {
|
||||
/// The numeric value failed conversion to js_int::Int.
|
||||
IntConvert,
|
||||
|
||||
/// The `CanonicalJsonValue` being serialized was larger than 65,535 bytes.
|
||||
JsonSize,
|
||||
|
||||
/// An error occurred while serializing/deserializing.
|
||||
SerDe(JsonError),
|
||||
}
|
||||
@ -40,7 +22,6 @@ impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Error::IntConvert => f.write_str("number found is not a valid `js_int::Int`"),
|
||||
Error::JsonSize => f.write_str("JSON is larger than 65,535 byte max"),
|
||||
Error::SerDe(err) => write!(f, "serde Error: {}", err),
|
||||
}
|
||||
}
|
||||
@ -67,10 +48,7 @@ mod tests {
|
||||
use js_int::int;
|
||||
use serde_json::{from_str as from_json_str, json, to_string as to_json_string};
|
||||
|
||||
use super::{
|
||||
to_canonical_value, to_string as to_canonical_json_string, try_from_json_map,
|
||||
value::CanonicalJsonValue,
|
||||
};
|
||||
use super::{to_canonical_value, try_from_json_map, value::CanonicalJsonValue};
|
||||
|
||||
#[test]
|
||||
fn serialize_canon() {
|
||||
@ -82,7 +60,7 @@ mod tests {
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
let ser = to_canonical_json_string(&json).unwrap();
|
||||
let ser = to_json_string(&json).unwrap();
|
||||
let back = from_json_str::<CanonicalJsonValue>(&ser).unwrap();
|
||||
|
||||
assert_eq!(json, back);
|
||||
|
@ -20,7 +20,7 @@ pub mod urlencoded;
|
||||
pub use buf::{json_to_buf, slice_to_buf};
|
||||
pub use can_be_empty::{is_empty, CanBeEmpty};
|
||||
pub use canonical_json::{
|
||||
to_canonical_value, to_string as to_canonical_json_string, try_from_json_map,
|
||||
to_canonical_value, try_from_json_map,
|
||||
value::{CanonicalJsonValue, Object as CanonicalJsonObject},
|
||||
Error as CanonicalJsonError,
|
||||
};
|
||||
|
@ -24,6 +24,10 @@ pub enum Error {
|
||||
/// [`SplitError`] wrapper.
|
||||
#[error("Split error: {0}")]
|
||||
SplitError(#[from] SplitError),
|
||||
|
||||
/// PDU was too large
|
||||
#[error("PDU is larger than maximum of 65535 bytes")]
|
||||
PduSize,
|
||||
}
|
||||
|
||||
/// All errors related to JSON validation/parsing.
|
||||
@ -69,11 +73,6 @@ pub enum JsonError {
|
||||
with_key: String,
|
||||
},
|
||||
|
||||
/// A derivative error from [`ruma_serde::CanonicalJsonError`],
|
||||
/// captured here.
|
||||
#[error("Canonical JSON error: {0}")]
|
||||
CanonicalJson(#[from] ruma_serde::CanonicalJsonError),
|
||||
|
||||
/// A more generic JSON error from [`serde_json`].
|
||||
#[error(transparent)]
|
||||
Serde(#[from] serde_json::Error),
|
||||
|
@ -10,8 +10,8 @@ use std::{
|
||||
use base64::{decode_config, encode_config, Config, STANDARD_NO_PAD, URL_SAFE_NO_PAD};
|
||||
use ed25519_dalek::Digest;
|
||||
use ruma_identifiers::{EventId, RoomVersionId, ServerNameBox, UserId};
|
||||
use ruma_serde::{to_canonical_json_string, CanonicalJsonObject, CanonicalJsonValue};
|
||||
use serde_json::from_str as from_json_str;
|
||||
use ruma_serde::{CanonicalJsonObject, CanonicalJsonValue};
|
||||
use serde_json::{from_str as from_json_str, to_string as to_json_string};
|
||||
use sha2::Sha256;
|
||||
|
||||
use crate::{
|
||||
@ -21,6 +21,8 @@ use crate::{
|
||||
Error, JsonError, JsonType, ParseError, VerificationError,
|
||||
};
|
||||
|
||||
const MAX_PDU_BYTES: usize = 65_535;
|
||||
|
||||
/// The fields that are allowed to remain in an event during redaction.
|
||||
static ALLOWED_KEYS: &[&str] = &[
|
||||
"event_id",
|
||||
@ -149,7 +151,7 @@ where
|
||||
let maybe_unsigned_entry = object.remove_entry("unsigned");
|
||||
|
||||
// Get the canonical JSON string.
|
||||
let json = to_canonical_json_string(object).map_err(JsonError::CanonicalJson)?;
|
||||
let json = to_json_string(object).map_err(JsonError::Serde)?;
|
||||
|
||||
// Sign the canonical JSON string.
|
||||
let signature = key_pair.sign(json.as_bytes());
|
||||
@ -204,10 +206,14 @@ pub fn canonical_json(object: &CanonicalJsonObject) -> Result<String, Error> {
|
||||
|
||||
/// Uses a set of public keys to verify a signed JSON object.
|
||||
///
|
||||
/// Unlike `content_hash` and `reference_hash`, this function does not report an error if the
|
||||
/// canonical JSON is larger than 65535 bytes; this function may be used for requests that are
|
||||
/// larger than just one PDU's maximum size.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * public_key_map: A map from entity identifiers to a map from key identifiers to public keys.
|
||||
/// Generally, entity identifiers are server names—the host/IP/port of a homeserver (e.g.
|
||||
/// Generally, entity identifiers are server names — the host/IP/port of a homeserver (e.g.
|
||||
/// "example.com") for which a signature must be verified. Key identifiers for each server (e.g.
|
||||
/// "ed25519:1") then map to their respective public keys.
|
||||
/// * object: The JSON object that was signed.
|
||||
@ -334,8 +340,16 @@ where
|
||||
/// # Parameters
|
||||
///
|
||||
/// object: A JSON object to generate a content hash for.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the event is too large.
|
||||
pub fn content_hash(object: &CanonicalJsonObject) -> Result<String, Error> {
|
||||
let json = canonical_json_with_fields_to_remove(object, CONTENT_HASH_FIELDS_TO_REMOVE)?;
|
||||
if json.len() > MAX_PDU_BYTES {
|
||||
return Err(Error::PduSize);
|
||||
}
|
||||
|
||||
let hash = Sha256::digest(json.as_bytes());
|
||||
|
||||
Ok(encode_config(&hash, STANDARD_NO_PAD))
|
||||
@ -355,7 +369,7 @@ pub fn content_hash(object: &CanonicalJsonObject) -> Result<String, Error> {
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if redaction fails.
|
||||
/// Returns an error if the event is too large or redaction fails.
|
||||
pub fn reference_hash(
|
||||
value: &CanonicalJsonObject,
|
||||
version: &RoomVersionId,
|
||||
@ -364,6 +378,9 @@ pub fn reference_hash(
|
||||
|
||||
let json =
|
||||
canonical_json_with_fields_to_remove(&redacted_value, REFERENCE_HASH_FIELDS_TO_REMOVE)?;
|
||||
if json.len() > MAX_PDU_BYTES {
|
||||
return Err(Error::PduSize);
|
||||
}
|
||||
|
||||
let hash = Sha256::digest(json.as_bytes());
|
||||
|
||||
@ -671,8 +688,9 @@ struct SignatureAndPubkey<'a> {
|
||||
public_key: &'a String,
|
||||
}
|
||||
|
||||
/// Internal implementation detail of the canonical JSON algorithm. Allows customization of the
|
||||
/// fields that will be removed before serializing.
|
||||
/// Internal implementation detail of the canonical JSON algorithm.
|
||||
///
|
||||
/// Allows customization of the fields that will be removed before serializing.
|
||||
fn canonical_json_with_fields_to_remove(
|
||||
object: &CanonicalJsonObject,
|
||||
fields: &[&str],
|
||||
@ -683,7 +701,7 @@ fn canonical_json_with_fields_to_remove(
|
||||
owned_object.remove(*field);
|
||||
}
|
||||
|
||||
to_canonical_json_string(&owned_object).map_err(|e| Error::Json(e.into()))
|
||||
to_json_string(&owned_object).map_err(|e| Error::Json(e.into()))
|
||||
}
|
||||
|
||||
/// Redacts an event using the rules specified in the Matrix client-server specification.
|
||||
|
Loading…
x
Reference in New Issue
Block a user