1// Copyright (c) 2024 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7use xso::{error::Error, AsXml, AsXmlText, FromXml, FromXmlText};
8
9use crate::ns;
10use alloc::borrow::Cow;
11
12/// One type of channel-binding, as [defined by the IANA](https://www.iana.org/assignments/channel-binding-types/channel-binding-types.xhtml)
13#[derive(Debug, Clone, PartialEq)]
14pub enum Type {
15 /// The tls-unique channel binding.
16 TlsUnique,
17
18 /// The tls-server-end-point channel binding.
19 TlsServerEndPoint,
20
21 /// The tls-unique-for-telnet channel binding.
22 TlsUniqueForTelnet,
23
24 /// The EKM value obtained from the current TLS connection.
25 ///
26 /// See RFC9266.
27 TlsExporter,
28}
29
30impl FromXmlText for Type {
31 fn from_xml_text(s: String) -> Result<Type, Error> {
32 Ok(match s.as_ref() {
33 "tls-unique" => Type::TlsUnique,
34 "tls-server-end-point" => Type::TlsServerEndPoint,
35 "tls-unique-for-telnet" => Type::TlsUniqueForTelnet,
36 "tls-exporter" => Type::TlsExporter,
37
38 _ => return Err(Error::Other("Unknown value '{s}' for 'type' attribute.")),
39 })
40 }
41}
42
43impl AsXmlText for Type {
44 fn as_xml_text(&self) -> Result<Cow<'_, str>, Error> {
45 Ok(Cow::Borrowed(match self {
46 Type::TlsUnique => "tls-unique",
47 Type::TlsServerEndPoint => "tls-server-end-point",
48 Type::TlsUniqueForTelnet => "tls-unique-for-telnet",
49 Type::TlsExporter => "tls-exporter",
50 }))
51 }
52}
53
54/// Stream feature listing the channel-binding types supported by the server.
55#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
56#[xml(namespace = ns::SASL_CB, name = "sasl-channel-binding")]
57pub struct SaslChannelBinding {
58 /// The list of channel-binding types supported by the server.
59 #[xml(extract(n = .., name = "channel-binding", fields(attribute(name = "type", type_ = Type))))]
60 pub types: Vec<Type>,
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66 use minidom::Element;
67
68 #[cfg(target_pointer_width = "64")]
69 #[test]
70 fn test_size() {
71 assert_size!(Type, 1);
72 assert_size!(SaslChannelBinding, 24);
73 }
74
75 #[cfg(target_pointer_width = "32")]
76 #[test]
77 fn test_size() {
78 assert_size!(Type, 1);
79 assert_size!(SaslChannelBinding, 12);
80 }
81
82 #[test]
83 fn test_simple() {
84 let elem: Element = "<sasl-channel-binding xmlns='urn:xmpp:sasl-cb:0'><channel-binding type='tls-server-end-point'/><channel-binding type='tls-exporter'/></sasl-channel-binding>".parse().unwrap();
85 let sasl_cb = SaslChannelBinding::try_from(elem).unwrap();
86 assert_eq!(sasl_cb.types, [Type::TlsServerEndPoint, Type::TlsExporter]);
87 }
88}