From 1367764f856122d632c1f0823b5ee898f244fd63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Sch=C3=A4fer?= Date: Wed, 26 Jun 2024 19:35:56 +0200 Subject: [PATCH] parsers: use Base64 codec to derive more things --- parsers/src/bob.rs | 76 ++++++++++------ parsers/src/cert_management.rs | 19 ++-- parsers/src/hashes.rs | 57 ++++++++---- parsers/src/ibb.rs | 32 +++---- parsers/src/legacy_omemo.rs | 156 ++++++++++++++++----------------- parsers/src/openpgp.rs | 19 ++-- parsers/src/sasl.rs | 90 +++++++++---------- parsers/src/util/macros.rs | 23 +++++ 8 files changed, 268 insertions(+), 204 deletions(-) diff --git a/parsers/src/bob.rs b/parsers/src/bob.rs index 1138e75370252243c2831e7f7f6869031946dd60..b2317df54613af177ea0e15e60031c89c96f0ed7 100644 --- a/parsers/src/bob.rs +++ b/parsers/src/bob.rs @@ -4,11 +4,12 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use xso::{error::Error, text::Base64, FromXml, FromXmlText, IntoXml, IntoXmlText}; + use crate::hashes::{Algo, Hash}; -use crate::util::text_node_codecs::{Base64, Codec}; +use crate::ns; use minidom::IntoAttributeValue; use std::str::FromStr; -use xso::error::Error; /// A Content-ID, as defined in RFC2111. /// @@ -49,6 +50,23 @@ impl FromStr for ContentId { } } +impl FromXmlText for ContentId { + fn from_xml_text(value: String) -> Result { + value.parse().map_err(Error::text_parse_error) + } +} + +impl IntoXmlText for ContentId { + fn into_xml_text(self) -> Result { + let algo = match self.hash.algo { + Algo::Sha_1 => "sha1", + Algo::Sha_256 => "sha256", + _ => unimplemented!(), + }; + Ok(format!("{}+{}@bob.xmpp.org", algo, self.hash.to_hex())) + } +} + impl IntoAttributeValue for ContentId { fn into_attribute_value(self) -> Option { let algo = match self.hash.algo { @@ -60,30 +78,32 @@ impl IntoAttributeValue for ContentId { } } -generate_element!( - /// Request for an uncached cid file. - Data, "data", BOB, - attributes: [ - /// The cid in question. - cid: Required = "cid", - - /// How long to cache it (in seconds). - max_age: Option = "max-age", - - /// The MIME type of the data being transmitted. - /// - /// See the [IANA MIME Media Types Registry][1] for a list of - /// registered types, but unregistered or yet-to-be-registered are - /// accepted too. - /// - /// [1]: - type_: Option = "type" - ], - text: ( - /// The actual data. - data: Base64 - ) -); +/// Request for an uncached cid file. +#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[xml(namespace = ns::BOB, name = "data")] +pub struct Data { + /// The cid in question. + #[xml(attribute)] + pub cid: ContentId, + + /// How long to cache it (in seconds). + #[xml(attribute(default, name = "max-age"))] + pub max_age: Option, + + /// The MIME type of the data being transmitted. + /// + /// See the [IANA MIME Media Types Registry][1] for a list of + /// registered types, but unregistered or yet-to-be-registered are + /// accepted too. + /// + /// [1]: + #[xml(attribute(default, name = "type"))] + pub type_: Option, + + /// The actual data. + #[xml(text = Base64)] + pub data: Vec, +} #[cfg(test)] mod tests { @@ -169,7 +189,7 @@ mod tests { #[test] fn unknown_child() { - let elem: Element = "" + let elem: Element = "" .parse() .unwrap(); let error = Data::try_from(elem).unwrap_err(); @@ -177,6 +197,6 @@ mod tests { FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; - assert_eq!(message, "Unknown child in data element."); + assert_eq!(message, "Unknown child in Data element."); } } diff --git a/parsers/src/cert_management.rs b/parsers/src/cert_management.rs index 5f8b1c8b1aaedc3f1ea3c3f76c91c85067786c7f..f40d450e3068e8744954aa04eded65e42e1d94c9 100644 --- a/parsers/src/cert_management.rs +++ b/parsers/src/cert_management.rs @@ -4,11 +4,10 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use xso::{FromXml, IntoXml}; +use xso::{text::Base64, FromXml, IntoXml}; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; -use crate::util::text_node_codecs::{Base64, Codec}; generate_elem_id!( /// The name of a certificate. @@ -17,14 +16,14 @@ generate_elem_id!( SASL_CERT ); -generate_element!( - /// An X.509 certificate. - Cert, "x509cert", SASL_CERT, - text: ( - /// The BER X.509 data. - data: Base64 - ) -); +/// An X.509 certificate. +#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[xml(namespace = ns::SASL_CERT, name = "x509cert")] +pub struct Cert { + /// The BER X.509 data. + #[xml(text = Base64)] + pub data: Vec, +} generate_element!( /// For the client to upload an X.509 certificate. diff --git a/parsers/src/hashes.rs b/parsers/src/hashes.rs index 16ff927de3e27ffc94aa1da857cca4cac1e730f3..7585af715d90b24b2755c5a9d43c0c011f11fb47 100644 --- a/parsers/src/hashes.rs +++ b/parsers/src/hashes.rs @@ -4,15 +4,15 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use xso::{FromXmlText, IntoXmlText}; +use xso::{error::Error, text::Base64, FromXml, FromXmlText, IntoXml, IntoXmlText}; -use crate::util::text_node_codecs::{Base64, Codec}; use base64::{engine::general_purpose::STANDARD as Base64Engine, Engine}; use minidom::IntoAttributeValue; use std::num::ParseIntError; use std::ops::{Deref, DerefMut}; use std::str::FromStr; -use xso::error::Error; + +use crate::ns; /// List of the algorithms we support, or Unknown. #[allow(non_camel_case_types)] @@ -91,25 +91,46 @@ impl From for String { } } +impl FromXmlText for Algo { + fn from_xml_text(value: String) -> Result { + value.parse().map_err(Error::text_parse_error) + } +} + +impl IntoXmlText for Algo { + fn into_xml_text(self) -> Result { + Ok(String::from(match self { + Algo::Sha_1 => "sha-1", + Algo::Sha_256 => "sha-256", + Algo::Sha_512 => "sha-512", + Algo::Sha3_256 => "sha3-256", + Algo::Sha3_512 => "sha3-512", + Algo::Blake2b_256 => "blake2b-256", + Algo::Blake2b_512 => "blake2b-512", + Algo::Unknown(text) => return Ok(text), + })) + } +} + impl IntoAttributeValue for Algo { fn into_attribute_value(self) -> Option { Some(String::from(self)) } } -generate_element!( - /// This element represents a hash of some data, defined by the hash - /// algorithm used and the computed value. - Hash, "hash", HASHES, - attributes: [ - /// The algorithm used to create this hash. - algo: Required = "algo" - ], - text: ( - /// The hash value, as a vector of bytes. - hash: Base64 - ) -); +/// This element represents a hash of some data, defined by the hash +/// algorithm used and the computed value. +#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[xml(namespace = ns::HASHES, name = "hash")] +pub struct Hash { + /// The algorithm used to create this hash. + #[xml(attribute)] + pub algo: Algo, + + /// The hash value, as a vector of bytes. + #[xml(text = Base64)] + pub hash: Vec, +} impl Hash { /// Creates a [struct@Hash] element with the given algo and data. @@ -281,7 +302,7 @@ mod tests { #[test] fn test_invalid_child() { - let elem: Element = "" + let elem: Element = "" .parse() .unwrap(); let error = Hash::try_from(elem).unwrap_err(); @@ -289,6 +310,6 @@ mod tests { FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; - assert_eq!(message, "Unknown child in hash element."); + assert_eq!(message, "Unknown child in Hash element."); } } diff --git a/parsers/src/ibb.rs b/parsers/src/ibb.rs index afcb2883fc8055f355bcbed7f199f3fb9993f085..7e937f6ef0e30922845c5a8455cd20a6ec3971b6 100644 --- a/parsers/src/ibb.rs +++ b/parsers/src/ibb.rs @@ -4,11 +4,10 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use xso::{FromXml, IntoXml}; +use xso::{text::Base64, FromXml, IntoXml}; use crate::iq::IqSetPayload; use crate::ns; -use crate::util::text_node_codecs::{Base64, Codec}; generate_id!( /// An identifier matching a stream. @@ -44,21 +43,22 @@ attributes: [ impl IqSetPayload for Open {} -generate_element!( /// Exchange a chunk of data in an open stream. -Data, "data", IBB, - attributes: [ - /// Sequence number of this chunk, must wraparound after 65535. - seq: Required = "seq", - - /// The identifier of the stream on which data is being exchanged. - sid: Required = "sid" - ], - text: ( - /// Vector of bytes to be exchanged. - data: Base64 - ) -); +#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[xml(namespace = ns::IBB, name = "data")] +pub struct Data { + /// Sequence number of this chunk, must wraparound after 65535. + #[xml(attribute)] + pub seq: u16, + + /// The identifier of the stream on which data is being exchanged. + #[xml(attribute)] + pub sid: StreamId, + + /// Vector of bytes to be exchanged. + #[xml(text = Base64)] + pub data: Vec, +} impl IqSetPayload for Data {} diff --git a/parsers/src/legacy_omemo.rs b/parsers/src/legacy_omemo.rs index 6b6442e9b0541cf151760835087d2104918d1709..4260c0c15aaf1bfa9687c4db5308df8dd775f283 100644 --- a/parsers/src/legacy_omemo.rs +++ b/parsers/src/legacy_omemo.rs @@ -4,12 +4,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use xso::{FromXml, IntoXml}; +use xso::{text::Base64, FromXml, IntoXml}; use crate::message::MessagePayload; use crate::ns; use crate::pubsub::PubSubPayload; -use crate::util::text_node_codecs::{Base64, Codec}; /// Element of the device list #[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] @@ -32,38 +31,38 @@ generate_element!( impl PubSubPayload for DeviceList {} -generate_element!( - /// SignedPreKey public key - /// Part of a device's bundle - SignedPreKeyPublic, "signedPreKeyPublic", LEGACY_OMEMO, - attributes: [ - /// SignedPreKey id - signed_pre_key_id: Option = "signedPreKeyId" - ], - text: ( - /// Serialized PublicKey - data: Base64 - ) -); +/// SignedPreKey public key +/// Part of a device's bundle +#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[xml(namespace = ns::LEGACY_OMEMO, name = "signedPreKeyPublic")] +pub struct SignedPreKeyPublic { + /// SignedPreKey id + #[xml(attribute(default, name = "signedPreKeyId"))] + pub signed_pre_key_id: Option, -generate_element!( - /// SignedPreKey signature - /// Part of a device's bundle - SignedPreKeySignature, "signedPreKeySignature", LEGACY_OMEMO, - text: ( - /// Signature bytes - data: Base64 - ) -); + /// Serialized PublicKey + #[xml(text = Base64)] + pub data: Vec, +} -generate_element!( - /// Part of a device's bundle - IdentityKey, "identityKey", LEGACY_OMEMO, - text: ( - /// Serialized PublicKey - data: Base64 - ) -); +/// SignedPreKey signature +/// Part of a device's bundle +#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[xml(namespace = ns::LEGACY_OMEMO, name = "signedPreKeySignature")] +pub struct SignedPreKeySignature { + /// Signature bytes + #[xml(text = Base64)] + pub data: Vec, +} + +/// Part of a device's bundle +#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[xml(namespace = ns::LEGACY_OMEMO, name = "identityKey")] +pub struct IdentityKey { + /// Serialized PublicKey + #[xml(text = Base64)] + pub data: Vec, +} generate_element!( /// List of (single use) PreKeys @@ -75,19 +74,19 @@ generate_element!( ] ); -generate_element!( - /// PreKey public key - /// Part of a device's bundle - PreKeyPublic, "preKeyPublic", LEGACY_OMEMO, - attributes: [ - /// PreKey id - pre_key_id: Required = "preKeyId", - ], - text: ( - /// Serialized PublicKey - data: Base64 - ) -); +/// PreKey public key +/// Part of a device's bundle +#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[xml(namespace = ns::LEGACY_OMEMO, name = "preKeyPublic")] +pub struct PreKeyPublic { + /// PreKey id + #[xml(attribute = "preKeyId")] + pub pre_key_id: u32, + + /// Serialized PublicKey + #[xml(text = Base64)] + pub data: Vec, +} generate_element!( /// A collection of publicly accessible data that can be used to build a session with a device, namely its public IdentityKey, a signed PreKey with corresponding signature, and a list of (single use) PreKeys. @@ -123,14 +122,14 @@ generate_element!( ] ); -generate_element!( - /// IV used for payload encryption - IV, "iv", LEGACY_OMEMO, - text: ( - /// IV bytes - data: Base64 - ) -); +/// IV used for payload encryption +#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[xml(namespace = ns::LEGACY_OMEMO, name = "iv")] +pub struct IV { + /// IV bytes + #[xml(text = Base64)] + pub data: Vec, +} generate_attribute!( /// prekey attribute for the key element. @@ -139,33 +138,34 @@ generate_attribute!( bool ); -generate_element!( - /// Part of the OMEMO element header - Key, "key", LEGACY_OMEMO, - attributes: [ - /// The device id this key is encrypted for. - rid: Required = "rid", +/// Part of the OMEMO element header +#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[xml(namespace = ns::LEGACY_OMEMO, name = "key")] +pub struct Key { + /// The device id this key is encrypted for. + #[xml(attribute)] + pub rid: u32, - /// The key element MUST be tagged with a prekey attribute set to true - /// if a PreKeySignalMessage is being used. - prekey: Default = "prekey", - ], - text: ( - /// The 16 bytes key and the GCM authentication tag concatenated together - /// and encrypted using the corresponding long-standing SignalProtocol - /// session - data: Base64 - ) -); + /// The key element MUST be tagged with a prekey attribute set to true + /// if a PreKeySignalMessage is being used. + #[xml(attribute(default))] + pub prekey: IsPreKey, -generate_element!( - /// The encrypted message body - Payload, "payload", LEGACY_OMEMO, - text: ( - /// Encrypted with AES-128 in Galois/Counter Mode (GCM) - data: Base64 - ) -); + /// The 16 bytes key and the GCM authentication tag concatenated together + /// and encrypted using the corresponding long-standing SignalProtocol + /// session + #[xml(text = Base64)] + pub data: Vec, +} + +/// The encrypted message body +#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[xml(namespace = ns::LEGACY_OMEMO, name = "payload")] +pub struct Payload { + /// Encrypted with AES-128 in Galois/Counter Mode (GCM) + #[xml(text = Base64)] + pub data: Vec, +} generate_element!( /// An OMEMO element, which can be either a MessageElement (with payload), diff --git a/parsers/src/openpgp.rs b/parsers/src/openpgp.rs index e96bca7f5a169f9045fbb0ac438753f9cad65926..d6c521bc6d4afded0eed52c08c16de4032d4b59d 100644 --- a/parsers/src/openpgp.rs +++ b/parsers/src/openpgp.rs @@ -4,22 +4,21 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use xso::{FromXml, IntoXml}; +use xso::{text::Base64, FromXml, IntoXml}; use crate::date::DateTime; use crate::ns; use crate::pubsub::PubSubPayload; -use crate::util::text_node_codecs::{Base64, Codec}; +/// Data contained in the PubKey element // TODO: Merge this container with the PubKey struct -generate_element!( - /// Data contained in the PubKey element - PubKeyData, "data", OX, - text: ( - /// Base64 data - data: Base64 - ) -); +#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[xml(namespace = ns::OX, name = "data")] +pub struct PubKeyData { + /// Base64 data + #[xml(text = Base64)] + pub data: Vec, +} generate_element!( /// Pubkey element to be used in PubSub publish payloads. diff --git a/parsers/src/sasl.rs b/parsers/src/sasl.rs index 892a919b6ef315c8475b5e0344e5fdaf5987448f..f27e3d8c30534e7254d42ec2cd371055730b2a85 100644 --- a/parsers/src/sasl.rs +++ b/parsers/src/sasl.rs @@ -4,13 +4,15 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use xso::{FromXml, IntoXml}; +use xso::{ + error::{Error, FromElementError}, + text::Base64, + FromXml, IntoXml, +}; use crate::ns; -use crate::util::text_node_codecs::{Base64, Codec}; use crate::Element; use std::collections::BTreeMap; -use xso::error::{Error, FromElementError}; generate_attribute!( /// The list of available SASL mechanisms. @@ -44,41 +46,41 @@ generate_attribute!( } ); -generate_element!( - /// The first step of the SASL process, selecting the mechanism and sending - /// the first part of the handshake. - Auth, "auth", SASL, - attributes: [ - /// The mechanism used. - mechanism: Required = "mechanism" - ], - text: ( - /// The content of the handshake. - data: Base64 - ) -); +/// The first step of the SASL process, selecting the mechanism and sending +/// the first part of the handshake. +#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[xml(namespace = ns::SASL, name = "auth")] +pub struct Auth { + /// The mechanism used. + #[xml(attribute)] + pub mechanism: Mechanism, + + /// The content of the handshake. + #[xml(text = Base64)] + pub data: Vec, +} -generate_element!( - /// In case the mechanism selected at the [auth](struct.Auth.html) step - /// requires a second step, the server sends this element with additional - /// data. - Challenge, "challenge", SASL, - text: ( - /// The challenge data. - data: Base64 - ) -); +/// In case the mechanism selected at the [auth](struct.Auth.html) step +/// requires a second step, the server sends this element with additional +/// data. +#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[xml(namespace = ns::SASL, name = "challenge")] +pub struct Challenge { + /// The challenge data. + #[xml(text = Base64)] + pub data: Vec, +} -generate_element!( - /// In case the mechanism selected at the [auth](struct.Auth.html) step - /// requires a second step, this contains the client’s response to the - /// server’s [challenge](struct.Challenge.html). - Response, "response", SASL, - text: ( - /// The response data. - data: Base64 - ) -); +/// In case the mechanism selected at the [auth](struct.Auth.html) step +/// requires a second step, this contains the client’s response to the +/// server’s [challenge](struct.Challenge.html). +#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[xml(namespace = ns::SASL, name = "response")] +pub struct Response { + /// The response data. + #[xml(text = Base64)] + pub data: Vec, +} /// Sent by the client at any point after [auth](struct.Auth.html) if it /// wants to cancel the current authentication process. @@ -86,14 +88,14 @@ generate_element!( #[xml(namespace = ns::SASL, name = "abort")] pub struct Abort; -generate_element!( - /// Sent by the server on SASL success. - Success, "success", SASL, - text: ( - /// Possible data sent on success. - data: Base64 - ) -); +/// Sent by the server on SASL success. +#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[xml(namespace = ns::SASL, name = "success")] +pub struct Success { + /// Possible data sent on success. + #[xml(text = Base64)] + pub data: Vec, +} generate_element_enum!( /// List of possible failure conditions for SASL. diff --git a/parsers/src/util/macros.rs b/parsers/src/util/macros.rs index 8aed09df8c3dca6f011e83276e5cf09a12338c77..8f33acbb3bc8697e7fdc33994e7f5a4c233e7cdd 100644 --- a/parsers/src/util/macros.rs +++ b/parsers/src/util/macros.rs @@ -211,6 +211,29 @@ macro_rules! generate_attribute { }) } } + impl ::xso::FromXmlText for $elem { + fn from_xml_text(s: String) -> Result<$elem, xso::error::Error> { + match s.parse::().map_err(xso::error::Error::text_parse_error)? { + true => Ok(Self::True), + false => Ok(Self::False), + } + } + } + impl ::xso::IntoXmlText for $elem { + fn into_xml_text(self) -> Result { + match self { + Self::True => Ok("true".to_owned()), + Self::False => Ok("false".to_owned()), + } + } + + fn into_optional_xml_text(self) -> Result, xso::error::Error> { + match self { + Self::True => Ok(Some("true".to_owned())), + Self::False => Ok(None), + } + } + } impl ::minidom::IntoAttributeValue for $elem { fn into_attribute_value(self) -> Option { match self {