From 32e8f2ec2e0497a28e4edb52c3c80abb412becbc Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 30 Jun 2024 18:07:00 +0200 Subject: [PATCH] xmpp-parsers: Implement XEP-0484: Fast Authentication Streamlining Tokens This specification defines a token-based method to streamline authentication in XMPP, allowing fully authenticated stream establishment within a single round-trip. --- parsers/ChangeLog | 2 + parsers/doap.xml | 8 +++ parsers/src/fast.rs | 116 ++++++++++++++++++++++++++++++++++++++++++++ parsers/src/lib.rs | 3 ++ parsers/src/ns.rs | 3 ++ 5 files changed, 132 insertions(+) create mode 100644 parsers/src/fast.rs diff --git a/parsers/ChangeLog b/parsers/ChangeLog index 54215339..9768f2d2 100644 --- a/parsers/ChangeLog +++ b/parsers/ChangeLog @@ -1,5 +1,7 @@ Version xxx: 0000-00-00 Authors + * New parsers/serialisers: + - Fast Authentication Streamlining Tokens (XEP-0484) * Improvements: - Re-export the jid module entirely. - Add serde feature, passed to jid crate diff --git a/parsers/doap.xml b/parsers/doap.xml index d54e2c0a..11795578 100644 --- a/parsers/doap.xml +++ b/parsers/doap.xml @@ -608,6 +608,14 @@ NEXT + + + + complete + 0.1.1 + NEXT + + diff --git a/parsers/src/fast.rs b/parsers/src/fast.rs new file mode 100644 index 00000000..e287cf98 --- /dev/null +++ b/parsers/src/fast.rs @@ -0,0 +1,116 @@ +// Copyright (c) 2024 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// 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 crate::date::DateTime; +use crate::ns; + +generate_elem_id!( + /// A `` element, describing one mechanism allowed by the server to authenticate. + Mechanism, "mechanism", FAST +); + +// TODO: Replace this with a proper bool once we can derive FromXml and IntoXml on FastQuery. +generate_attribute!( + /// Whether TLS zero-roundtrip is possible. + Tls0Rtt, "tls-0rtt", bool +); + +generate_element!( +/// This is the `` element sent by the server as a SASL2 inline feature. +FastQuery, "fast", FAST, +attributes: [ + /// Whether TLS zero-roundtrip is possible. + tls_0rtt: Default = "tls-0rtt", +], +children: [ + /// A list of `` elements, listing all server allowed mechanisms. + mechanisms: Vec = ("mechanism", FAST) => Mechanism +] +); + +/// This is the `` element the client MUST include within its SASL2 authentication request. +#[derive(FromXml, IntoXml, Debug, Clone, PartialEq)] +#[xml(namespace = ns::FAST, name = "fast")] +pub struct FastResponse { + /// Servers MUST reject any authentication requests received via TLS 0-RTT payloads that do not + /// include a 'count' attribute, or where the count is less than or equal to a count that has + /// already been processed for this token. This protects against replay attacks that 0-RTT is + /// susceptible to. + #[xml(attribute)] + pub count: u32, + + /// If true and the client has successfully authenticated, the server MUST invalidate the + /// token. + #[xml(attribute(default))] + pub invalidate: bool, +} + +/// This is the `` element sent by the client in the SASL2 authenticate step. +#[derive(FromXml, IntoXml, Debug, Clone, PartialEq)] +#[xml(namespace = ns::FAST, name = "request-token")] +pub struct RequestToken { + /// This element MUST contain a 'mechanism' attribute, the value of which MUST be one of the + /// FAST mechanisms advertised by the server. + #[xml(attribute)] + pub mechanism: String, +} + +/// This is the `` element sent by the server on successful SASL2 authentication containing +/// a `` element. +#[derive(FromXml, IntoXml, Debug, Clone, PartialEq)] +#[xml(namespace = ns::FAST, name = "token")] +pub struct Token { + /// The secret token to be used for subsequent authentications, as generated by the server. + #[xml(attribute)] + pub token: String, + + /// The timestamp at which the token will expire. + #[xml(attribute)] + pub expiry: DateTime, +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::Element; + use std::str::FromStr; + + #[test] + fn test_simple() { + let elem: Element = "FOO-BAR" + .parse() + .unwrap(); + let request = FastQuery::try_from(elem).unwrap(); + assert_eq!(request.tls_0rtt, Tls0Rtt::False); + assert_eq!(request.mechanisms, [Mechanism(String::from("FOO-BAR"))]); + + let elem: Element = "" + .parse() + .unwrap(); + let response = FastResponse::try_from(elem).unwrap(); + assert_eq!(response.count, 123); + assert_eq!(response.invalidate, false); + + let elem: Element = "" + .parse() + .unwrap(); + let request_token = RequestToken::try_from(elem).unwrap(); + assert_eq!(request_token.mechanism, "FOO-BAR"); + + let elem: Element = + "" + .parse() + .unwrap(); + let token = Token::try_from(elem).unwrap(); + assert_eq!(token.token, "ABCD"); + assert_eq!( + token.expiry, + DateTime::from_str("2024-06-30T17:13:57+02:00").unwrap() + ); + } +} diff --git a/parsers/src/lib.rs b/parsers/src/lib.rs index 04dd15f4..cf2fd3b5 100644 --- a/parsers/src/lib.rs +++ b/parsers/src/lib.rs @@ -266,3 +266,6 @@ pub mod mam_prefs; /// XEP-0444: Message Reactions pub mod reactions; + +/// XEP-0484: Fast Authentication Streamlining Tokens +pub mod fast; diff --git a/parsers/src/ns.rs b/parsers/src/ns.rs index 88dbe67d..2915f233 100644 --- a/parsers/src/ns.rs +++ b/parsers/src/ns.rs @@ -302,3 +302,6 @@ pub const DEFAULT_NS: &str = JABBER_CLIENT; /// "jabber:component:accept" when the component feature is enabled. #[cfg(feature = "component")] pub const DEFAULT_NS: &str = COMPONENT_ACCEPT; + +/// XEP-0484: Fast Authentication Streamlining Tokens +pub const FAST: &str = "urn:xmpp:fast:0";