serde: Fix lots of issues from previous commit
This commit is contained in:
parent
0dbeac8505
commit
b7bcecbb77
@ -1,4 +1,6 @@
|
|||||||
use std::fmt;
|
//!Transparent base64 encoding / decoding as part of (de)serialization.
|
||||||
|
|
||||||
|
use std::{fmt, marker::PhantomData};
|
||||||
|
|
||||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
@ -9,37 +11,50 @@ use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
|||||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Base64<C = Standard, B = Vec<u8>> {
|
pub struct Base64<C = Standard, B = Vec<u8>> {
|
||||||
bytes: B,
|
bytes: B,
|
||||||
|
_phantom_conf: PhantomData<*mut C>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Config used for the [`Base64`] type.
|
||||||
pub trait Base64Config {
|
pub trait Base64Config {
|
||||||
const CONF: base64::Config;
|
/// The config as a constant.
|
||||||
|
///
|
||||||
|
/// Opaque so our interface is not tied to the base64 crate version.
|
||||||
|
#[doc(hidden)]
|
||||||
|
const CONF: Conf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct Conf(base64::Config);
|
||||||
|
|
||||||
/// Standard base64 character set without padding.
|
/// Standard base64 character set without padding.
|
||||||
///
|
///
|
||||||
/// Allows trailing bits in decoding for maximum compatibility.
|
/// Allows trailing bits in decoding for maximum compatibility.
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
|
// Easier than implementing these all for Base64 manually to avoid the `C: Trait` bounds.
|
||||||
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Standard;
|
pub struct Standard;
|
||||||
|
|
||||||
impl Base64Config for Standard {
|
impl Base64Config for Standard {
|
||||||
// See https://github.com/matrix-org/matrix-doc/issues/3211
|
// See https://github.com/matrix-org/matrix-doc/issues/3211
|
||||||
const CONF: base64::Config = base64::STANDARD_NO_PAD.decode_allow_trailing_bits(true);
|
const CONF: Conf = Conf(base64::STANDARD_NO_PAD.decode_allow_trailing_bits(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Url-safe base64 character set without padding.
|
/// Url-safe base64 character set without padding.
|
||||||
///
|
///
|
||||||
/// Allows trailing bits in decoding for maximum compatibility.
|
/// Allows trailing bits in decoding for maximum compatibility.
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
|
// Easier than implementing these all for Base64 manually to avoid the `C: Trait` bounds.
|
||||||
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct UrlSafe;
|
pub struct UrlSafe;
|
||||||
|
|
||||||
impl Base64Config for UrlSafe {
|
impl Base64Config for UrlSafe {
|
||||||
const CONF: base64::Config = base64::URL_SAFE_NO_PAD.decode_allow_trailing_bits(true);
|
const CONF: Conf = Conf(base64::URL_SAFE_NO_PAD.decode_allow_trailing_bits(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: AsRef<[u8]>> Base64<B> {
|
impl<C: Base64Config, B: AsRef<[u8]>> Base64<C, B> {
|
||||||
/// Create a `Base64` instance from raw bytes, to be base64-encoded in serialialization.
|
/// Create a `Base64` instance from raw bytes, to be base64-encoded in serialialization.
|
||||||
pub fn new(bytes: B) -> Self {
|
pub fn new(bytes: B) -> Self {
|
||||||
Self { bytes }
|
Self { bytes, _phantom_conf: PhantomData }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to the raw bytes held by this `Base64` instance.
|
/// Get a reference to the raw bytes held by this `Base64` instance.
|
||||||
@ -49,42 +64,42 @@ impl<B: AsRef<[u8]>> Base64<B> {
|
|||||||
|
|
||||||
/// Encode the bytes contained in this `Base64` instance to unpadded base64.
|
/// Encode the bytes contained in this `Base64` instance to unpadded base64.
|
||||||
pub fn encode(&self) -> String {
|
pub fn encode(&self) -> String {
|
||||||
base64::encode_config(&self.bytes, BASE64_CONFIG)
|
base64::encode_config(&self.bytes, C::CONF.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B> Base64<B> {
|
impl<C, B> Base64<C, B> {
|
||||||
/// Get the raw bytes held by this `Base64` instance.
|
/// Get the raw bytes held by this `Base64` instance.
|
||||||
pub fn into_inner(self) -> B {
|
pub fn into_inner(self) -> B {
|
||||||
self.bytes
|
self.bytes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Base64 {
|
impl<C: Base64Config> Base64<C> {
|
||||||
/// Create a `Base64` instance containing an empty `Vec<u8>`.
|
/// Create a `Base64` instance containing an empty `Vec<u8>`.
|
||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
Self { bytes: Vec::new() }
|
Self::new(Vec::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse some base64-encoded data to create a `Base64` instance.
|
/// Parse some base64-encoded data to create a `Base64` instance.
|
||||||
pub fn parse(encoded: impl AsRef<[u8]>) -> Result<Self, base64::DecodeError> {
|
pub fn parse(encoded: impl AsRef<[u8]>) -> Result<Self, base64::DecodeError> {
|
||||||
base64::decode_config(encoded, BASE64_CONFIG).map(Self::new)
|
base64::decode_config(encoded, C::CONF.0).map(Self::new)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: AsRef<[u8]>> fmt::Debug for Base64<B> {
|
impl<C: Base64Config, B: AsRef<[u8]>> fmt::Debug for Base64<C, B> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.encode().fmt(f)
|
self.encode().fmt(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: AsRef<[u8]>> fmt::Display for Base64<B> {
|
impl<C: Base64Config, B: AsRef<[u8]>> fmt::Display for Base64<C, B> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.encode().fmt(f)
|
self.encode().fmt(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Base64 {
|
impl<'de, C: Base64Config> Deserialize<'de> for Base64<C> {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
@ -94,7 +109,7 @@ impl<'de> Deserialize<'de> for Base64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: AsRef<[u8]>> Serialize for Base64<B> {
|
impl<C: Base64Config, B: AsRef<[u8]>> Serialize for Base64<C, B> {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
|
@ -9,7 +9,7 @@ use std::{
|
|||||||
|
|
||||||
use base64::{encode_config, STANDARD_NO_PAD, URL_SAFE_NO_PAD};
|
use base64::{encode_config, STANDARD_NO_PAD, URL_SAFE_NO_PAD};
|
||||||
use ruma_identifiers::{EventId, RoomVersionId, ServerName, UserId};
|
use ruma_identifiers::{EventId, RoomVersionId, ServerName, UserId};
|
||||||
use ruma_serde::{Base64, CanonicalJsonObject, CanonicalJsonValue};
|
use ruma_serde::{base64::Standard, Base64, CanonicalJsonObject, CanonicalJsonValue};
|
||||||
use serde_json::{from_str as from_json_str, to_string as to_json_string};
|
use serde_json::{from_str as from_json_str, to_string as to_json_string};
|
||||||
use sha2::{digest::Digest, Sha256};
|
use sha2::{digest::Digest, Sha256};
|
||||||
|
|
||||||
@ -291,7 +291,7 @@ pub fn verify_json(
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let signature = Base64::parse(signature)
|
let signature = Base64::<Standard>::parse(signature)
|
||||||
.map_err(|e| ParseError::base64("signature", signature, e))?;
|
.map_err(|e| ParseError::base64("signature", signature, e))?;
|
||||||
|
|
||||||
verify_json_with(
|
verify_json_with(
|
||||||
@ -332,8 +332,6 @@ where
|
|||||||
|
|
||||||
/// Creates a *content hash* for an event.
|
/// Creates a *content hash* for an event.
|
||||||
///
|
///
|
||||||
/// Returns the hash as a base64-encoded string, using the standard character set, without padding.
|
|
||||||
///
|
|
||||||
/// The content hash of an event covers the complete event including the unredacted contents. It is
|
/// The content hash of an event covers the complete event including the unredacted contents. It is
|
||||||
/// used during federation and is described in the Matrix server-server specification.
|
/// used during federation and is described in the Matrix server-server specification.
|
||||||
///
|
///
|
||||||
@ -344,7 +342,7 @@ where
|
|||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error if the event is too large.
|
/// Returns an error if the event is too large.
|
||||||
pub fn content_hash(object: &CanonicalJsonObject) -> Result<Base64<[u8; 32]>, Error> {
|
pub fn content_hash(object: &CanonicalJsonObject) -> Result<Base64<Standard, [u8; 32]>, Error> {
|
||||||
let json = canonical_json_with_fields_to_remove(object, CONTENT_HASH_FIELDS_TO_REMOVE)?;
|
let json = canonical_json_with_fields_to_remove(object, CONTENT_HASH_FIELDS_TO_REMOVE)?;
|
||||||
if json.len() > MAX_PDU_BYTES {
|
if json.len() > MAX_PDU_BYTES {
|
||||||
return Err(Error::PduSize);
|
return Err(Error::PduSize);
|
||||||
@ -656,8 +654,8 @@ pub fn verify_event(
|
|||||||
|
|
||||||
let public_key = signature_and_pubkey.public_key;
|
let public_key = signature_and_pubkey.public_key;
|
||||||
|
|
||||||
let signature =
|
let signature = Base64::<Standard>::parse(signature)
|
||||||
Base64::parse(signature).map_err(|e| ParseError::base64("signature", signature, e))?;
|
.map_err(|e| ParseError::base64("signature", signature, e))?;
|
||||||
|
|
||||||
verify_json_with(
|
verify_json_with(
|
||||||
&Ed25519Verifier,
|
&Ed25519Verifier,
|
||||||
@ -669,7 +667,7 @@ pub fn verify_event(
|
|||||||
|
|
||||||
let calculated_hash = content_hash(object)?;
|
let calculated_hash = content_hash(object)?;
|
||||||
|
|
||||||
if let Ok(hash) = Base64::parse(hash) {
|
if let Ok(hash) = Base64::<Standard>::parse(hash) {
|
||||||
if hash.as_bytes() == calculated_hash.as_bytes() {
|
if hash.as_bytes() == calculated_hash.as_bytes() {
|
||||||
return Ok(Verified::All);
|
return Ok(Verified::All);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user