otrr/
session.rs

1// SPDX-License-Identifier: LGPL-3.0-only
2
3use std::{collections, rc::Rc};
4
5use crate::{
6    ake::{AKEContext, CryptographicMaterial},
7    clientprofile::{ClientProfile, ClientProfilePayload},
8    dake::{DAKEContext, MixedKeyMaterial},
9    encoding::{MessageFlags, OTRDecoder, OTREncoder},
10    fragment::{self, FragmentError},
11    instancetag::{self, InstanceTag, INSTANCE_ZERO},
12    messages::{
13        self, encode_message, serialize_message, EncodedMessage, EncodedMessageType, MessageType,
14    },
15    protocol::{self, Message, ProtocolMaterial},
16    smp::{self, SMPStatus},
17    smp4::{self, SMP4Status},
18    utils, Host, OTRError, Policy, ProtocolStatus, UserMessage, Version, SSID, SUPPORTED_VERSIONS,
19};
20
21pub struct Account {
22    host: Rc<dyn Host>,
23    details: Rc<AccountDetails>,
24    profile: ClientProfile,
25    sessions: collections::HashMap<Vec<u8>, Session>,
26}
27
28// TODO set up a heartbeat timer that checks up on sessions and sends heartbeat messages if necessary.
29impl Account {
30    /// `new` creates a new Account instance.
31    ///
32    /// # Errors
33    /// In case of failure to reconstruct client profile.
34    pub fn new(account: Vec<u8>, policy: Policy, host: Rc<dyn Host>) -> Result<Self, OTRError> {
35        let sessions = collections::HashMap::new();
36        let profile = if let Ok(restored) = Self::restore_clientprofile(host.as_ref()) {
37            log::trace!("Account: attempting to restore client-profile from host.");
38            restored
39        } else {
40            log::trace!("Account: host provided zero bytes, incorrect or expired profile. Constructing new client profile.");
41            Self::generate_clientprofile(host.as_ref())
42        };
43        let details = Rc::new(AccountDetails {
44            policy,
45            tag: profile.owner_tag,
46            account,
47        });
48        Ok(Self {
49            host,
50            details,
51            profile,
52            sessions,
53        })
54    }
55
56    // TODO how to approach client profile renewals?
57    fn restore_clientprofile(host: &dyn Host) -> Result<ClientProfile, OTRError> {
58        log::trace!("Account: restoring existing client profile.");
59        let encoded_profile = host.client_profile();
60        let mut decoder = OTRDecoder::new(&encoded_profile);
61        let payload = ClientProfilePayload::decode(&mut decoder)?;
62        decoder.done()?;
63        payload.validate()
64    }
65
66    fn generate_clientprofile(host: &dyn Host) -> ClientProfile {
67        const DEFAULT_EXPIRATION: u64 = 7 * 24 * 3600;
68        let tag = instancetag::random_tag();
69        let identity_keypair = host.keypair_identity();
70        let identity_public = identity_keypair.public().clone();
71        let forging_public = host.keypair_forging().public().clone();
72        let mut versions = vec![Version::V4];
73        let legacy_keypair = host.keypair();
74        if legacy_keypair.is_some() {
75            versions.push(Version::V3);
76        }
77        let expiration = i64::try_from(
78            std::time::SystemTime::now()
79                .checked_add(std::time::Duration::new(DEFAULT_EXPIRATION, 0))
80                .unwrap()
81                .duration_since(std::time::UNIX_EPOCH)
82                .unwrap()
83                .as_secs(),
84        )
85        .expect("BUG: working under the assumption that the duration calculation fits in an i64.");
86        let profile = ClientProfile::new(
87            tag,
88            identity_public,
89            forging_public,
90            versions,
91            expiration,
92            legacy_keypair.map(|keypair| keypair.public_key().clone()),
93        ).expect("BUG: failed to construct new client-profile for our client. This only happens in case of bad input such as illegal keypairs.");
94        let payload = profile.sign(identity_keypair, legacy_keypair);
95        host.update_client_profile(OTREncoder::new().write_encodable(&payload).to_vec());
96        profile
97    }
98
99    #[must_use]
100    pub fn instance_tag(&self) -> InstanceTag {
101        self.details.tag
102    }
103
104    #[must_use]
105    pub fn policy(&self) -> Policy {
106        self.details.policy
107    }
108
109    #[must_use]
110    pub fn session(&mut self, address: &[u8]) -> &mut Session {
111        self.sessions
112            .entry(Vec::from(address))
113            .or_insert(Session::new(
114                Rc::clone(&self.host),
115                Rc::clone(&self.details),
116                Vec::from(address),
117            ))
118    }
119
120    /// expire expires all timed-out (in secs) sessions and their instances of an account.
121    ///
122    /// This method handles a complete account. (Alternatively, there is `Session::expire`.)
123    pub fn expire(&mut self, timeout: u64) {
124        // TODO this is currently not thread-safe. Depending on how the heart-beat timer is going to work, this can cause problems.
125        for (_, session) in &mut self.sessions {
126            session.expire(timeout);
127        }
128    }
129}
130
131pub struct Session {
132    host: Rc<dyn Host>,
133    details: Rc<AccountDetails>,
134    /// address contains the address of the remote party (chat account).
135    address: Vec<u8>,
136    /// instances contains all individual instances (clients) that have been
137    /// encountered. Instance 0 is used for clients that have not yet announced
138    /// their instance tag. Typically, before or during initial stages of OTR.
139    instances: collections::HashMap<InstanceTag, Instance>,
140    /// `whitespace_tagged` indicates whether or not a whitespace-tagged plaintext message was sent
141    /// already. This field is shared with the method call that constructs the actual
142    /// whitespace-tagged message whenever the opportunity is there. This field is shared such that
143    /// sending a whitespace-tagged message (which is not specific to an instance) is common
144    /// knowledge among all instances.
145    // TODO `whitespace_tagged` is never reset to false. (After reentering MSGSTATE_PLAINTEXT)
146    whitespace_tagged: bool,
147}
148
149// TODO we need to ensure the (legacy) DSA keypair is available when OTR3 interaction is executed. The logic assumes that the keypair is present.
150impl Session {
151    fn new(host: Rc<dyn Host>, details: Rc<AccountDetails>, address: Vec<u8>) -> Session {
152        let mut instances = collections::HashMap::new();
153        instances.insert(
154            INSTANCE_ZERO,
155            Instance::new(
156                Rc::clone(&details),
157                Rc::clone(&host),
158                address.clone(),
159                INSTANCE_ZERO,
160            ),
161        );
162        Self {
163            host,
164            details,
165            address,
166            instances,
167            whitespace_tagged: false,
168        }
169    }
170
171    /// `instances` returns a list of known instance tags. The session may be in any
172    /// state of the protocol, i.e. `MSGSTATE_PLAINTEXT`, `MSGSTATE_ENCRYPTED`, `MSGSTATE_FINISHED`.
173    /// However, the fact that a session (known by instance tag) exists, means that this instance
174    /// tag was once revealed.
175    #[must_use]
176    pub fn instances(&self) -> Vec<InstanceTag> {
177        let mut sessions = Vec::<InstanceTag>::new();
178        for k in self.instances.keys() {
179            if *k == INSTANCE_ZERO {
180                continue;
181            }
182            sessions.push(*k);
183        }
184        sessions
185    }
186
187    /// expire expires all instances of a session if timeout (in secs) is reached.
188    ///
189    /// This method handles a single session, i.e. multiple instances. (Alternatively, there is
190    /// `Account::expire` that call this method.)
191    pub fn expire(&mut self, timeout: u64) {
192        // TODO this is currently not thread-safe. Depending on how the heart-beat timer is going to work, this can cause problems.
193        for (_, instance) in &mut self.instances {
194            instance.expire(timeout);
195        }
196    }
197
198    /// Query status (protocol status) for a particular instance. Returns status if the instance is
199    /// known.
200    pub fn status(&self, instance: InstanceTag) -> Option<ProtocolStatus> {
201        self.instances.get(&instance).map(Instance::status)
202    }
203
204    /// `receive` processes a raw-bytes payload for possible OTR content. Receive should be called
205    /// for any received messages such that the protocol may transparently handle any OTR protocol
206    /// message, as well as warn the client about plaintext message received at unexpected times.
207    ///
208    /// # Errors
209    ///
210    /// Will return `OTRError` on any deviating circumstances of the protocol, such as failed AKE
211    /// negotiations, failed SMP negotiations, incorrectly formatted messages, protocol violations,
212    /// etc. Most errors will contain an instance tag when an established OTR session is involved.
213    ///
214    /// # Panics
215    ///
216    /// Will panic on incorrect internal state or uses. It should not panic on any user input, as
217    /// these are typically the chat network messages therefore out of the clients control.
218    // REMARK fuzzing target
219    // TODO double-check if we correctly mitigate if we keep receiving messages with sender id '0' as this is only valid for OTR protocol 2 use-cases, which we don't support.
220    // TODO ideally, one active AKE should exclude the other, such that no two processes can be in progress simultaneously.
221    #[allow(clippy::too_many_lines)]
222    pub fn receive(&mut self, payload: &[u8]) -> Result<UserMessage, OTRError> {
223        log::debug!("Processing incoming message ..");
224        if !self.details.policy.contains(Policy::ALLOW_V3)
225            && !self.details.policy.contains(Policy::ALLOW_V4)
226        {
227            // OTR: if no version is allowed according to policy, do not do any handling at all.
228            return Ok(UserMessage::Plaintext(Vec::from(payload)));
229        }
230        if fragment::match_fragment(payload) {
231            log::debug!("Processing OTR fragment ..");
232            let Some(fragment) = fragment::parse(payload) else {
233                log::debug!("Not a valid/supported fragment.");
234                return Ok(UserMessage::None);
235            };
236            fragment::verify(&fragment).or(Err(OTRError::ProtocolViolation("Invalid fragment")))?;
237            if fragment.receiver != INSTANCE_ZERO && fragment.receiver != self.details.tag {
238                // ignore instance tag ZERO as this is only relevant for OTRv2 and we do not
239                // support this.
240                return Err(OTRError::MessageForOtherInstance);
241            }
242            let details = Rc::clone(&self.details);
243            let instance = self.instances.entry(fragment.sender).or_insert_with(|| {
244                Instance::new(
245                    details,
246                    Rc::clone(&self.host),
247                    self.address.clone(),
248                    fragment.sender,
249                )
250            });
251            return match instance.assembler.assemble(&fragment) {
252                Ok(assembled) => {
253                    if fragment::match_fragment(&assembled) {
254                        return Err(OTRError::ProtocolViolation("Assembled fragments assemble into a fragment. This is disallowed by the specification."));
255                    }
256                    self.receive(&assembled)
257                }
258                Err(FragmentError::IncompleteResult | FragmentError::UnexpectedFragment) => {
259                    // We've received a message fragment, but not enough to reassemble a message, so
260                    // return early with no actual result and tell the client to wait for more
261                    // fragments to arrive. (Or we have received an unexpected fragment, therefore
262                    // reset everything and forget what we had up to now.)
263                    Ok(UserMessage::None)
264                }
265                Err(FragmentError::InvalidData) => {
266                    // Given that this is (merely) a fragment, just discard it and do not send an
267                    // error message, as the error message would wreak havoc on the (still
268                    // functional) encrypted session.
269                    Err(OTRError::ProtocolViolation(
270                        "Fragment contains invalid data.",
271                    ))
272                }
273            };
274        }
275        // TODO can we handle possible errors produced here to reset whitespace_tagged, respond with OTR Error, etc?
276        match messages::parse(payload)? {
277            MessageType::Error(error) => {
278                log::debug!("Processing OTR Error message ..");
279                if self.details.policy.contains(Policy::ERROR_START_AKE) {
280                    self.query()?;
281                }
282                Ok(UserMessage::Error(error))
283            }
284            MessageType::Plaintext(content) => {
285                log::debug!("Processing plaintext message ..");
286                if self.has_sessions() || self.details.policy.contains(Policy::REQUIRE_ENCRYPTION) {
287                    Ok(UserMessage::WarningUnencrypted(content))
288                } else {
289                    Ok(UserMessage::Plaintext(content))
290                }
291            }
292            MessageType::Tagged(versions, content) => {
293                log::debug!("Processing whitespace-tagged message ..");
294                if self.details.policy.contains(Policy::WHITESPACE_START_AKE) {
295                    if let Some(selected) = select_version(&self.details.policy, &versions) {
296                        self.initiate(&selected, INSTANCE_ZERO)?;
297                    }
298                }
299                if self.has_sessions() || self.details.policy.contains(Policy::REQUIRE_ENCRYPTION) {
300                    Ok(UserMessage::WarningUnencrypted(content))
301                } else {
302                    Ok(UserMessage::Plaintext(content))
303                }
304            }
305            MessageType::Query(versions) => {
306                log::debug!("Processing query message ..");
307                log::trace!("Query-message with versions {:?}", versions);
308                if let Some(selected) = select_version(&self.details.policy, &versions) {
309                    self.initiate(&selected, INSTANCE_ZERO)?;
310                }
311                Ok(UserMessage::None)
312            }
313            MessageType::Encoded(EncodedMessage {
314                version: _,
315                sender: 0,
316                receiver: _,
317                message: _,
318            }) => {
319                log::debug!("Encoded message with sender-tag 0. This is illegal in OTR protocol (starting with version 3).");
320                Ok(UserMessage::None)
321            }
322            MessageType::Encoded(
323                msg @ EncodedMessage {
324                    version: Version::V3,
325                    sender: _,
326                    receiver: _,
327                    message: EncodedMessageType::DHKey(_),
328                },
329            ) => {
330                log::debug!("Processing OTR-encoded D-H Commit message (with possible need to transfer AKEContext)…");
331                // When a DH-Commit message was sent with receiver tag ZERO, then we may receive
332                // any number of DH-Key messages in response. That is, a DH-Key message for each
333                // client of the account that receives the DH-Commit message. (Potentially even
334                // after the fact if client OTR plug-in incorrectly responds to history (replay)
335                // of chat. Only upon receiving do we obtain the instance tag such that we can
336                // redirect processing to a dedicated instance.
337                self.verify_encoded_message_header(&msg)?;
338                if !self.details.policy.contains(Policy::ALLOW_V3) {
339                    return Ok(UserMessage::None);
340                }
341                // TODO DH-Key (responses) may be received multiple times (multiple instances, multiple repeats). Do we need to take these cases into account when handling? (temporary dh keypair and `r` value are same/reused for all cases, same CTR value used for all cases)
342                // Transfer is only supported in `AKEState::AwaitingDHKey`. Therefore, result
343                // indicates whether transfer is possible.
344                let result_context = self
345                    .instances
346                    .get(&INSTANCE_ZERO)
347                    .expect("BUG: instance 0 should always exist.")
348                    .transfer_ake_context();
349                let instance = self.instances.entry(msg.sender).or_insert_with(|| {
350                    Instance::new(
351                        Rc::clone(&self.details),
352                        Rc::clone(&self.host),
353                        self.address.clone(),
354                        msg.sender,
355                    )
356                });
357                if let Ok(context) = result_context {
358                    instance.adopt_ake_context(context);
359                }
360                instance.handle(msg)
361            }
362            MessageType::Encoded(
363                msg @ EncodedMessage {
364                    version: Version::V4,
365                    sender: _,
366                    receiver: _,
367                    message: EncodedMessageType::AuthR(_),
368                },
369            ) => {
370                // TODO should we also check protocol version, to see if AuthR message is sent correctly?
371                log::debug!("Processing OTR-encoded Auth-R message (with possible need to transfer DAKEContext)…");
372                log::trace!("Auth-R message: {msg:?}");
373                self.verify_encoded_message_header(&msg)?;
374                if !self.details.policy.contains(Policy::ALLOW_V4) {
375                    return Ok(UserMessage::None);
376                }
377                // TODO Auth-R (responses) may be received multiple times (multiple instances, multiple repeats). Do we need to take these cases into account when handling? (temporary dh keypair and `r` value are same/reused for all cases, same CTR value used for all cases)
378                // Transfer is only supported in `AKEState::AwaitingDHKey`. Therefore, result
379                // indicates whether transfer is possible.
380                let result_context = self
381                    .instances
382                    .get(&INSTANCE_ZERO)
383                    .expect("BUG: instance 0 should always exist.")
384                    .transfer_dake_context();
385                let instance = self.instances.entry(msg.sender).or_insert_with(|| {
386                    Instance::new(
387                        Rc::clone(&self.details),
388                        Rc::clone(&self.host),
389                        self.address.clone(),
390                        msg.sender,
391                    )
392                });
393                // TODO what if transfer isn't necessary? can we clearly distinguish between the two cases?
394                if let Ok(context) = result_context {
395                    instance.adopt_dake_context(context);
396                }
397                instance.handle(msg)
398            }
399            MessageType::Encoded(msg) => {
400                log::debug!("Processing OTR-encoded message…");
401                log::trace!("Encoded message: {msg:?}");
402                self.verify_encoded_message_header(&msg)?;
403                if msg.version == Version::V3 && !self.details.policy.contains(Policy::ALLOW_V3)
404                    || msg.version == Version::V4 && !self.details.policy.contains(Policy::ALLOW_V4)
405                {
406                    return Ok(UserMessage::None);
407                }
408                self.instances
409                    .entry(msg.sender)
410                    .or_insert_with(|| {
411                        Instance::new(
412                            Rc::clone(&self.details),
413                            Rc::clone(&self.host),
414                            self.address.clone(),
415                            msg.sender,
416                        )
417                    })
418                    .handle(msg)
419            }
420        }
421    }
422
423    fn verify_encoded_message_header(&self, msg: &EncodedMessage) -> Result<(), OTRError> {
424        match msg.version {
425            Version::None => {
426                return Err(OTRError::ProtocolViolation(
427                    "Encoded message must always have a protocol version.",
428                ))
429            }
430            Version::Unsupported(version) => return Err(OTRError::UnsupportedVersion(version)),
431            Version::V3 | Version::V4 => { /* This is acceptable. */ }
432        }
433        instancetag::verify(msg.sender).or(Err(OTRError::ProtocolViolation(
434            "Sender instance tag is illegal value",
435        )))?;
436        if msg.sender == INSTANCE_ZERO {
437            return Err(OTRError::ProtocolViolation("Sender instance tag is zero"));
438        }
439        instancetag::verify(msg.receiver).or(Err(OTRError::ProtocolViolation(
440            "Receiver instance tag is illegal value",
441        )))?;
442        if msg.receiver == 0
443            && !matches!(
444                msg.message,
445                EncodedMessageType::DHCommit(_) | EncodedMessageType::Identity(_)
446            )
447        {
448            return Err(OTRError::ProtocolViolation(
449                "Receiver instance tag is zero.",
450            ));
451        }
452        if msg.receiver > INSTANCE_ZERO && msg.receiver != self.details.tag {
453            return Err(OTRError::MessageForOtherInstance);
454        }
455        Ok(())
456    }
457
458    /// `send` processes plaintext message content (user input) through the current state of OTR to
459    /// ready them for sending. This involves possibly encrypting the plaintext message, possibly
460    /// triggering other protocol interactions, and so forth. Additionally, depending on set
461    /// policies, `send` may issue warnings or refuse to operate to ensure that the client operates
462    /// according to set policies.
463    ///
464    /// NOTE: for correctness of the OTR protocol, `0` (`NULL`) values in the user message will be
465    /// dropped. If the policy does not allow OTR to operate (all protocol versions disabled) then
466    /// user content will not be touched at all.
467    ///
468    /// # Errors
469    ///
470    /// Will return `OTRError` on any kind of special-case situations involving the OTR protocol,
471    /// such as protocol violations, inproper state, incorrect internal state (data), etc.
472    ///
473    /// # Panics
474    ///
475    /// Will panic on inappropriate user-input. Panics are most likely traced back to incorrect use.
476    // TODO do we check if message contains null-bytes? (Nulls are not allowed, because it's the separator between message and TLVs.)
477    pub fn send(
478        &mut self,
479        instance: InstanceTag,
480        content: &[u8],
481    ) -> Result<Vec<Vec<u8>>, OTRError> {
482        if !self.details.policy.contains(Policy::ALLOW_V3)
483            && !self.details.policy.contains(Policy::ALLOW_V4)
484        {
485            log::debug!("No protocol versions are allowed. OTR support is disabled.");
486            return Ok(vec![Vec::from(content)]);
487        }
488        let instance = self
489            .instances
490            .get_mut(&instance)
491            .ok_or(OTRError::UnknownInstance(instance))?;
492        // "If msgstate is MSGSTATE_PLAINTEXT:"
493        if self.details.policy.contains(Policy::REQUIRE_ENCRYPTION)
494            && instance.status() == ProtocolStatus::Plaintext
495        {
496            // "   If REQUIRE_ENCRYPTION is set:
497            //       Store the plaintext message for possible retransmission, and send a Query
498            //       Message."
499            self.query()?;
500            return Err(OTRError::PolicyRestriction(
501                "Encryption is required by policy, but no confidential session is established yet. Query-message has been sent to initiate OTR session.",
502            ));
503        }
504        // "If msgstate is MSGSTATE_ENCRYPTED:
505        //    Encrypt the message, and send it as a Data Message. Store the plaintext message for
506        //    possible retransmission.
507        //  If msgstate is MSGSTATE_FINISHED:
508        //    Inform the user that the message cannot be sent at this time. Store the plaintext
509        //    message for possible retransmission."
510        instance.send(&mut self.whitespace_tagged, content)
511    }
512
513    /// `initiate` initiates the OTR protocol for designated receiver.
514    ///
515    /// # Errors
516    /// In case of in-progress (D)AKE session, which requires manually aborting first.
517    // TODO now that `initiate` may return an error, check if this needs handling or whether propagation is fine.
518    pub fn initiate(
519        &mut self,
520        version: &Version,
521        receiver: InstanceTag,
522    ) -> Result<UserMessage, OTRError> {
523        self.instances
524            .entry(receiver)
525            .or_insert_with(|| {
526                Instance::new(
527                    Rc::clone(&self.details),
528                    Rc::clone(&self.host),
529                    self.address.clone(),
530                    receiver,
531                )
532            })
533            .initiate(version)
534    }
535
536    /// `ssid` returns the SSID used for verification in case of an established (encrypted) OTR
537    /// session.
538    ///
539    /// # Errors
540    ///
541    /// Will give an `OTRError::UnknownInstance` error in case of non-existing instance.
542    // TODO it is not possible to identify which half must be highlighted in the user interface.
543    pub fn ssid(&self, instance: InstanceTag) -> Result<SSID, OTRError> {
544        self.instances
545            .get(&instance)
546            .ok_or(OTRError::UnknownInstance(instance))?
547            .ssid()
548    }
549
550    fn get_instance(&mut self, instance: InstanceTag) -> Result<&mut Instance, OTRError> {
551        self.instances
552            .get_mut(&instance)
553            .ok_or(OTRError::UnknownInstance(instance))
554    }
555
556    /// `has_encrypted_sessions` checks if any instances are established or finished OTR sessions.
557    fn has_sessions(&self) -> bool {
558        self.instances.iter().any(|i| {
559            assert_eq!(*i.0, i.1.receiver);
560            assert!(
561                *i.0 != INSTANCE_ZERO || i.1.status() == ProtocolStatus::Plaintext,
562                "BUG: Given that we do not support OTR version 1 and 2, we expect instance 0 is Plaintext"
563            );
564            i.1.status() == ProtocolStatus::Encrypted || i.1.status() == ProtocolStatus::Finished
565        })
566    }
567
568    /// `end` ends the specified OTR session and resets the state back to plaintext. This means that
569    /// confidential communication ends and any subsequent message will be sent as plain text, i.e.
570    /// unencrypted. This function should only be called as a result of _direct user interaction_.
571    ///
572    /// In the case the other party ended/aborted the session, the session would transition to
573    /// `MSGSTATE_FINISHED`. In that case, too, `end` resets the session back to
574    /// `MSGSTATE_PLAINTEXT`
575    ///
576    /// # Errors
577    ///
578    /// Will return an error in case the specified instance does not exist.
579    pub fn end(&mut self, instance: InstanceTag) -> Result<UserMessage, OTRError> {
580        Ok(self.get_instance(instance)?.reset())
581    }
582
583    /// `query` sends a OTR query-message over the host's communication network in order to probe
584    /// for other parties that are willing to initiate an OTR session.
585    ///
586    /// # Errors
587    ///
588    /// Will return an error in case of no compatible errors.
589    pub fn query(&mut self) -> Result<(), OTRError> {
590        let accepted_versions = filter_versions(&self.details.policy, &SUPPORTED_VERSIONS);
591        if accepted_versions.is_empty() {
592            return Err(OTRError::UserError("No supported versions available."));
593        }
594        self.host.inject(
595            &self.address,
596            &serialize_message(&MessageType::Query(accepted_versions)),
597        );
598        Ok(())
599    }
600
601    /// `start_smp` initiates the Socialist Millionaires' Protocol for the specified instance. The
602    /// initiator immediately supplies a question (`question`, which is optional so may be
603    /// zero-length) and a `secret` which is the secret value that tested for in the SMP execution.
604    ///
605    /// # Errors
606    ///
607    /// Will return `OTRError` in case the instance does not exist, or the protocol is in an
608    /// incorrect state. An established encrypted OTR session is necessary to start SMP.
609    pub fn start_smp(
610        &mut self,
611        instance: InstanceTag,
612        secret: &[u8],
613        question: &[u8],
614    ) -> Result<(), OTRError> {
615        self.get_instance(instance)?.start_smp(secret, question)
616    }
617
618    /// `abort_smp` aborts an (in-progress) SMP session.
619    ///
620    /// # Errors
621    ///
622    /// Will return `OTRError` in case the specified instance is not a confidential session, i.e.
623    /// encrypted OTR session, and on any violations of the OTR protocol.
624    pub fn abort_smp(&mut self, instance: InstanceTag) -> Result<(), OTRError> {
625        self.get_instance(instance)?.abort_smp()
626    }
627}
628
629/// Instance serves a single communication session, ensuring that messages always travel between the same two clients.
630struct Instance {
631    details: Rc<AccountDetails>,
632    host: Rc<dyn Host>,
633    address: Vec<u8>,
634    receiver: InstanceTag,
635    // assembler for each instance, such that resets of the OTR3 assembler are per instance
636    assembler: fragment::Assembler,
637    state: Box<dyn protocol::ProtocolState>,
638    ake: AKEContext,
639    dake: DAKEContext,
640}
641
642/// `Instance` represents a single instance, a communication session with a single client of an
643/// account. The protocol assumes that multiple clients can be active at the same time for a single
644/// chat account.
645/// `Instance` expects to receive (as much as possible) preselected values to be used: selection,
646/// validation to be performed in `Session` if possible.
647impl Instance {
648    fn new(
649        details: Rc<AccountDetails>,
650        host: Rc<dyn Host>,
651        address: Vec<u8>,
652        receiver: InstanceTag,
653    ) -> Self {
654        Self {
655            ake: AKEContext::new(Rc::clone(&host)),
656            dake: DAKEContext::new(Rc::clone(&host)),
657            details,
658            host,
659            address,
660            receiver,
661            assembler: fragment::Assembler::new(),
662            state: protocol::new_state(),
663        }
664    }
665
666    fn status(&self) -> ProtocolStatus {
667        self.state.status()
668    }
669
670    fn expire(&mut self, timeout: u64) {
671        // TODO this is currently not thread-safe. Depending on how the heart-beat timer is going to work, this can cause problems.
672        if let Some((disconnect_msg, new_state)) = self.state.expire(timeout) {
673            let prev = core::mem::replace(
674                &mut self.state,
675                new_state as Box<dyn protocol::ProtocolState>,
676            );
677            self.inject(&prev.version(), self.receiver, disconnect_msg);
678        }
679    }
680
681    fn initiate(&mut self, version: &Version) -> Result<UserMessage, OTRError> {
682        let initiator = match version {
683            Version::V3 => self.ake.initiate(),
684            Version::V4 => self.dake.initiate()?,
685            Version::None | Version::Unsupported(_) => panic!("BUG: incorrect use of API"),
686        };
687        self.inject(version, self.receiver, initiator);
688        Ok(UserMessage::None)
689    }
690
691    fn transfer_ake_context(&self) -> Result<AKEContext, OTRError> {
692        assert_eq!(INSTANCE_ZERO, self.receiver);
693        self.ake.transfer().map_err(OTRError::AuthenticationError)
694    }
695
696    fn adopt_ake_context(&mut self, context: AKEContext) {
697        assert_ne!(0, self.receiver);
698        log::trace!("OTR AKE state transferred.");
699        self.ake = context;
700    }
701
702    fn transfer_dake_context(&self) -> Result<DAKEContext, OTRError> {
703        assert_eq!(INSTANCE_ZERO, self.receiver);
704        self.dake.transfer()
705    }
706
707    fn adopt_dake_context(&mut self, context: DAKEContext) {
708        assert_ne!(0, self.receiver);
709        log::trace!("OTRv4 DAKE state transferred.");
710        self.dake = context;
711    }
712
713    // TODO should established OTR sessions respond to query? (should not re-establish all active sessions, i.e. multiple instances)
714    #[allow(clippy::too_many_lines)]
715    fn handle(&mut self, encoded_message: EncodedMessage) -> Result<UserMessage, OTRError> {
716        // Given that we are processing an actual (OTR-)encoded message intended for this instance,
717        // we should reset the assembler now.
718        let version = encoded_message.version;
719        let sender = encoded_message.sender;
720        let receiver = encoded_message.receiver;
721        if version == Version::V3 {
722            self.assembler.cleanup(&Version::V3);
723        }
724        match (&version, encoded_message.message) {
725            (Version::V3, EncodedMessageType::DHCommit(msg)) => {
726                let response = self
727                    .ake
728                    .handle_dhcommit(msg).map_err(OTRError::AuthenticationError)?;
729                self.inject(&self.ake.version(), sender, response);
730                Ok(UserMessage::None)
731            }
732            (Version::V3, EncodedMessageType::DHKey(msg)) => {
733                let response = self
734                    .ake
735                    .handle_dhkey(msg).map_err(OTRError::AuthenticationError)?;
736                self.inject(&self.ake.version(), sender, response);
737                Ok(UserMessage::None)
738            }
739            (Version::V3, EncodedMessageType::RevealSignature(msg)) => {
740                let (CryptographicMaterial{ssid, our_dh, their_dh, their_dsa}, response) = self
741                    .ake
742                    .handle_reveal_signature(msg).map_err(OTRError::AuthenticationError)?;
743                self.state = self.state.secure(Rc::clone(&self.host), self.details.tag,
744                    encoded_message.sender, ProtocolMaterial::AKE { ssid, our_dh, their_dh, their_dsa });
745                assert_eq!(ProtocolStatus::Encrypted, self.state.status());
746                self.inject(&self.ake.version(), sender, response);
747                Ok(UserMessage::ConfidentialSessionStarted(self.receiver))
748            }
749            (Version::V3, EncodedMessageType::Signature(msg)) => {
750                let CryptographicMaterial{ssid, our_dh, their_dh, their_dsa} = self
751                    .ake
752                    .handle_signature(msg).map_err(OTRError::AuthenticationError)?;
753                self.state = self.state.secure(Rc::clone(&self.host), self.details.tag,
754                    encoded_message.sender, ProtocolMaterial::AKE { ssid, our_dh, their_dh, their_dsa });
755                assert_eq!(ProtocolStatus::Encrypted, self.state.status());
756                Ok(UserMessage::ConfidentialSessionStarted(self.receiver))
757            }
758            (Version::V3, EncodedMessageType::Data(msg)) => {
759                // NOTE that TLV 0 (Padding) and 1 (Disconnect) are already handled as part of the
760                // protocol. Other TLVs that are their own protocol or function, therefore must be
761                // handled separately.
762                let authenticator_data = messages::encode_authenticator_data(&version, sender, receiver, &msg);
763                let (message, transition) = self.state.handle(&msg, &authenticator_data);
764                if transition.is_some() {
765                    self.state = transition.unwrap();
766                }
767                match message {
768                    Ok(Message::Confidential(content, tlvs)) if tlvs.iter().any(smp::is_smp_tlv) => {
769                        if !msg.flags.contains(MessageFlags::IGNORE_UNREADABLE) {
770                            log::warn!("Other client did not set ignore-unreadable flag on SMP messages. This is preferred/recommended because these are OTR control messages, rather than user content.");
771                        }
772                        if !content.is_empty() {
773                            log::warn!("OTR3 SMP tlv messages are not expected to contain text content. This content is ignored.");
774                        }
775                        if tlvs.iter().filter(|t| smp::is_smp_tlv(t)).count() > 1 {
776                            // Given more than one SMP tlv, the order of processing impacts outcome.
777                            // The protocol only specifies that a single SMP tlv is expected at any
778                            // one time, so do not even attempt to process these. It is a violation.
779                            log::warn!("OTR3 more than one SMP tlv found. This is not expected according to the specification. Aborting further processing.");
780                            return Err(OTRError::ProtocolViolation("SMP: more than one SMP tlv found. This cannot occur when protocol is properly followed."));
781                        }
782                        let Ok(smp_context) = self.state.smp_mut() else {
783                            // SMP tlvs are only relevant in encrypted-messaging state. If we
784                            // transitioned away just now, then this SMP tlv no longer has any value
785                            // Log this somewhat peculiar circumstance and stop processing.
786                            log::warn!("OTR3 SMP state is no longer available. The state machine must have transitioned away before the SMP tlvs were processed. This seems to indicate a protocol violation. Ignoring this SMP tlv and returning result without user-content.");
787                            return Err(OTRError::ProtocolViolation("OTR3 SMP tlv being processed while the state machine has already transitioned away. This cannot happen in OTR unless the other client deviated from the protocol."));
788                        };
789                        // Socialist Millionaire Protocol (SMP) handling.
790                        let tlv = tlvs.into_iter().find(smp::is_smp_tlv).unwrap();
791                        if let Some(reply_tlv) = smp_context.handle(&tlv) {
792                            let otr_message = self.state.prepare(
793                                MessageFlags::IGNORE_UNREADABLE,
794                                &OTREncoder::new()
795                                    .write_u8(0)
796                                    .write_tlv(&reply_tlv)
797                                    .to_vec())?;
798                            // TODO is current self.state representative of version at time otr_message was constructed?
799                            self.inject(&self.state.version(), sender, otr_message);
800                        }
801                        match self.state.smp().unwrap().status() {
802                            SMPStatus::InProgress => Ok(UserMessage::None),
803                            SMPStatus::Completed => Ok(UserMessage::SMPSucceeded(self.receiver)),
804                            SMPStatus::Aborted(_) => Ok(UserMessage::SMPFailed(self.receiver)),
805                            SMPStatus::Initial => panic!("BUG: we should be able to reach after having processed an SMP message TLV."),
806                        }
807                    }
808                    Ok(Message::Confidential(content, tlvs)) => Ok(UserMessage::Confidential(self.receiver, content, tlvs)),
809                    Ok(Message::ConfidentialFinished(content)) => Ok(UserMessage::ConfidentialSessionFinished(self.receiver, content)),
810                    Err(OTRError::UnreadableMessage(_)) if msg.flags.contains(MessageFlags::IGNORE_UNREADABLE) => {
811                        // For an unreadable message, even if the IGNORE_UNREADABLE flag is set, we
812                        // need to send an OTR Error response, to indicate to the other user that
813                        // we no longer have a correctly established OTR session.
814                        self.host.inject(&self.address, &serialize_message(&MessageType::Error(
815                            Vec::from("unreadable message")
816                        )));
817                        Ok(UserMessage::None)
818                    }
819                    Err(OTRError::UnreadableMessage(_)) => {
820                        self.host.inject(&self.address, &serialize_message(&MessageType::Error(
821                            Vec::from("unreadable message")
822                        )));
823                        Err(OTRError::UnreadableMessage(self.receiver))
824                    }
825                    Err(error) => {
826                        // TODO do all these errors require Error Message response to other party?
827                        log::debug!("Received unexpected error-type: {:?}", &error);
828                        Err(error)
829                    }
830                }
831            }
832            (Version::V4, EncodedMessageType::Identity(message)) => {
833                let response = self.dake.handle_identity(message, &self.details.account, &self.address)?;
834                self.inject(&self.dake.version(), sender, response);
835                Ok(UserMessage::None)
836            }
837            (Version::V4, EncodedMessageType::AuthR(message)) => {
838                let (MixedKeyMaterial{ssid, double_ratchet, us, them}, response) = self.dake.handle_auth_r(message, &self.details.account, &self.address)?;
839                self.inject(&self.dake.version(), sender, response);
840                self.state = self.state.secure(Rc::clone(&self.host), self.details.tag, self.receiver, ProtocolMaterial::DAKE { ssid, double_ratchet, us, them });
841                // TODO is this assertion valid? (what if we perform new DAKE while in encrypted session?)
842                assert_eq!(ProtocolStatus::Encrypted, self.state.status());
843                Ok(UserMessage::ConfidentialSessionStarted(self.receiver))
844            }
845            (Version::V4, EncodedMessageType::AuthI(message)) => {
846                let MixedKeyMaterial{ssid, double_ratchet, us, them} = self.dake.handle_auth_i(message, &self.details.account, &self.address)?;
847                self.state = self.state.secure(Rc::clone(&self.host), self.details.tag, self.receiver, ProtocolMaterial::DAKE { ssid, double_ratchet, us, them });
848                // TODO is this assertion valid? (what if we perform new DAKE while in encrypted session?)
849                assert_eq!(ProtocolStatus::Encrypted, self.state.status());
850                Ok(UserMessage::ConfidentialSessionStarted(self.receiver))
851            }
852            (Version::V4, EncodedMessageType::Data4(msg)) => {
853                msg.validate()?;
854                // Note that TLV 0 (Padding) and 1 (Disconnect) are already handled as part of the
855                // protocol. Other TLVs that are their own protocol or function, therefore must be
856                // handled separately.
857                let authenticator_data = messages::encode_authenticator_data4(&version, sender, receiver, &msg);
858                let (message, transition) = self.state.handle4(&msg, &authenticator_data);
859                if transition.is_some() {
860                    self.state = transition.unwrap();
861                }
862                match message {
863                    Ok(Message::Confidential(content, tlvs)) if tlvs.iter().any(smp4::is_smp_tlv) => {
864                        if !msg.flags.contains(MessageFlags::IGNORE_UNREADABLE) {
865                            log::warn!("Other client did not set IGNORE_UNREADABLE flag on SMP messages. This is preferred/recommended because these are OTR control messages, rather than user content.");
866                        }
867                        if !content.is_empty() {
868                            log::warn!("OTRv4 SMP tlv messages are not expected to contain text content. This content is ignored.");
869                        }
870                        if tlvs.iter().filter(|t| smp4::is_smp_tlv(t)).count() > 1 {
871                            // Given more than one SMP tlv, the order of processing impacts outcome.
872                            // The protocol only specifies that a single SMP tlv is expected at any
873                            // one time, so do not even attempt to process these. It is a violation.
874                            log::warn!("OTRv4 more than one SMP tlv found. This is not expected according to the specification. Aborting further processing.");
875                            return Err(OTRError::ProtocolViolation("SMP: more than one SMP tlv found. This cannot occur when protocol is properly followed."));
876                        }
877                        let Ok(smp4_context) = self.state.smp4_mut() else {
878                            // SMP tlvs are only relevant in encrypted-messaging state. If we
879                            // transitioned away just now, then this SMP tlv no longer has any value
880                            // Log this somewhat peculiar circumstance and stop processing.
881                            log::warn!("OTRv4 SMP state is no longer available. The state machine must have transitioned away before the SMP tlvs were processed. This seems to indicate a protocol violation. Ignoring this SMP tlv and returning result without user-content.");
882                            return Err(OTRError::ProtocolViolation("OTRv4 SMP tlv being processed while the state machine has already transitioned away. This cannot happen in OTR unless the other client deviated from the protocol."));
883                        };
884                        // Socialist Millionaire Protocol (SMP) handling.
885                        let tlv = tlvs.into_iter().find(smp4::is_smp_tlv).unwrap();
886                        if let Some(response) = smp4_context.handle(&tlv) {
887                            let otr_message = self.state.prepare(
888                                MessageFlags::IGNORE_UNREADABLE,
889                                &OTREncoder::new()
890                                    .write_u8(0)
891                                    .write_tlv(&response)
892                                    .to_vec())?;
893                            // TODO is current self.state representative of version at time otr_message was constructed?
894                            self.inject(&self.state.version(), sender, otr_message);
895                        }
896                        match self.state.smp4().unwrap().status() {
897                            SMP4Status::InProgress => Ok(UserMessage::None),
898                            SMP4Status::Completed => Ok(UserMessage::SMPSucceeded(self.receiver)),
899                            SMP4Status::Aborted(_) => Ok(UserMessage::SMPFailed(self.receiver)),
900                            SMP4Status::Initial => panic!("BUG: we should be able to reach after having processed an SMP message TLV."),
901                        }
902                    }
903                    Ok(Message::Confidential(content, tlvs)) => Ok(UserMessage::Confidential(self.receiver, content, tlvs)),
904                    Ok(Message::ConfidentialFinished(content)) => Ok(UserMessage::ConfidentialSessionFinished(self.receiver, content)),
905                    Err(OTRError::UnreadableMessage(_)) if msg.flags.contains(MessageFlags::IGNORE_UNREADABLE) => {
906                        log::debug!("Received unreadable message with flags {}", msg.flags.bits());
907                        // For an unreadable message, even if the IGNORE_UNREADABLE flag is set, we
908                        // need to send an OTR Error response, to indicate to the other user that
909                        // we no longer have a correctly established OTR session.
910                        self.host.inject(&self.address, &serialize_message(&MessageType::Error(
911                            Vec::from("unreadable message")
912                        )));
913                        Ok(UserMessage::None)
914                    }
915                    Err(OTRError::UnreadableMessage(_)) => {
916                        self.host.inject(&self.address, &serialize_message(&MessageType::Error(
917                            Vec::from("unreadable message")
918                        )));
919                        Err(OTRError::UnreadableMessage(self.receiver))
920                    }
921                    Err(error) => {
922                        // TODO do all these errors require Error Message response to other party?
923                        log::debug!("Received unexpected error-type: {:?}", &error);
924                        Err(error)
925                    }
926                }
927            }
928            (_, EncodedMessageType::Unencoded(_)) => panic!("BUG: this message-type is used as a placeholder. It can never be an incoming message-type to be handled."),
929            _ => Err(OTRError::ProtocolViolation("Illegal encoded message. Message ignored."))
930        }
931    }
932
933    fn reset(&mut self) -> UserMessage {
934        let previous = self.state.status();
935        let version = self.state.version();
936        let (abortmsg, newstate) = self.state.finish();
937        self.state = newstate;
938        if previous == self.state.status() {
939            assert!(abortmsg.is_none());
940            return UserMessage::None;
941        }
942        if let Some(msg) = abortmsg {
943            self.inject(&version, self.receiver, msg);
944        }
945        UserMessage::Reset(self.receiver)
946    }
947
948    fn send(
949        &mut self,
950        whitespace_tagged: &mut bool,
951        plaintext: &[u8],
952    ) -> Result<Vec<Vec<u8>>, OTRError> {
953        let plaintext = utils::bytes::drop_by_value(plaintext, 0);
954        match self.state.prepare(MessageFlags::empty(), &plaintext)? {
955            EncodedMessageType::Unencoded(msg) => {
956                log::trace!("Message prepared as unencoded message.");
957                assert_eq!(
958                    ProtocolStatus::Plaintext,
959                    self.state.status(),
960                    "BUG: received undefined message type in status {:?}",
961                    self.state.status()
962                );
963                let versions = filter_versions(&self.details.policy, &SUPPORTED_VERSIONS);
964                let message = if self.details.policy.contains(Policy::SEND_WHITESPACE_TAG)
965                    && !*whitespace_tagged
966                    && !versions.is_empty()
967                {
968                    *whitespace_tagged = true;
969                    MessageType::Tagged(versions, msg)
970                } else {
971                    MessageType::Plaintext(msg)
972                };
973                Ok(vec![serialize_message(&message)])
974            }
975            message @ (EncodedMessageType::DHCommit(_)
976            | EncodedMessageType::DHKey(_)
977            | EncodedMessageType::RevealSignature(_)
978            | EncodedMessageType::Signature(_)) => {
979                log::trace!("Message prepared as OTR-encoded protocol message.");
980                let content = encode_message(
981                    &self.ake.version(),
982                    self.details.tag,
983                    self.receiver,
984                    message,
985                );
986                Ok(self.prepare_payloads(&self.ake.version(), content))
987            }
988            message @ (EncodedMessageType::Identity(_)
989            | EncodedMessageType::AuthR(_)
990            | EncodedMessageType::AuthI(_)) => {
991                log::trace!("Message prepared as OTR-encoded OTRv4 protocol message.");
992                let content = encode_message(
993                    &self.dake.version(),
994                    self.details.tag,
995                    self.receiver,
996                    message,
997                );
998                Ok(self.prepare_payloads(&self.dake.version(), content))
999            }
1000            message @ (EncodedMessageType::Data(_) | EncodedMessageType::Data4(_)) => {
1001                let content = encode_message(
1002                    &self.state.version(),
1003                    self.details.tag,
1004                    self.receiver,
1005                    message,
1006                );
1007                Ok(self.prepare_payloads(&self.state.version(), content))
1008            }
1009        }
1010    }
1011
1012    fn prepare_payloads(&self, version: &Version, payload: Vec<u8>) -> Vec<Vec<u8>> {
1013        let max_size = self.host.message_size();
1014        if payload.len() <= max_size {
1015            // send message-bytes as-is: fragmentation is not needed.
1016            vec![payload]
1017        } else {
1018            // fragmentation is needed: send multiple fragments instead.
1019            fragment::fragment(max_size, version, self.details.tag, self.receiver, &payload)
1020                .iter()
1021                .map(|f| OTREncoder::new().write_encodable(f).to_vec())
1022                .collect()
1023        }
1024    }
1025
1026    fn start_smp(&mut self, secret: &[u8], question: &[u8]) -> Result<(), OTRError> {
1027        // logic currently assumes that if the call to smp succeeds, that we are in an appropriate
1028        // state to send a message with appended TLV.
1029        let version = self.state.version();
1030        let tlv = match (&self.state.status(), &version) {
1031            (ProtocolStatus::Encrypted, Version::V3) => {
1032                self.state.smp_mut()?.initiate(secret, question)?
1033            }
1034            (ProtocolStatus::Encrypted, Version::V4) => {
1035                self.state.smp4_mut()?.initiate(secret, question)?
1036            }
1037            _ => {
1038                return Err(OTRError::IncorrectState(
1039                    "Session is not in a supported state.",
1040                ))
1041            }
1042        };
1043        let message = self.state.prepare(
1044            MessageFlags::IGNORE_UNREADABLE,
1045            &OTREncoder::new().write_u8(0).write_tlv(&tlv).to_vec(),
1046        )?;
1047        self.inject(&version, self.receiver, message);
1048        Ok(())
1049    }
1050
1051    fn ssid(&self) -> Result<SSID, OTRError> {
1052        match (&self.state.status(), &self.state.version()) {
1053            (ProtocolStatus::Encrypted, Version::V3) => Ok(self.state.smp()?.ssid()),
1054            (ProtocolStatus::Encrypted, Version::V4) => Ok(self.state.smp4()?.ssid()),
1055            _ => Err(OTRError::IncorrectState(
1056                "Session is not in a supported state to acquire SSID for SMP.",
1057            )),
1058        }
1059    }
1060
1061    fn abort_smp(&mut self) -> Result<(), OTRError> {
1062        let tlv = match (&self.state.status(), &self.state.version()) {
1063            (ProtocolStatus::Encrypted, Version::V3) => self.state.smp_mut()?.abort(),
1064            (ProtocolStatus::Encrypted, Version::V4) => self.state.smp4_mut()?.abort(),
1065            _ => {
1066                return Err(OTRError::IncorrectState(
1067                    "SMP is not available in current protocol state.",
1068                ))
1069            }
1070        };
1071        let msg = self
1072            .state
1073            .prepare(
1074                MessageFlags::IGNORE_UNREADABLE,
1075                &OTREncoder::new().write_u8(0).write_tlv(&tlv).to_vec(),
1076            )
1077            .unwrap();
1078        self.inject(&self.state.version(), self.receiver, msg);
1079        Ok(())
1080    }
1081
1082    fn inject(&self, version: &Version, receiver: InstanceTag, message: EncodedMessageType) {
1083        assert!(receiver != 0 && self.receiver == 0 || self.receiver == receiver);
1084        log::trace!(
1085            "Injecting encoded message with tag '{}' for '{}'.",
1086            self.details.tag,
1087            receiver
1088        );
1089        let content = encode_message(version, self.details.tag, self.receiver, message);
1090        let max_size = self.host.message_size();
1091        if content.len() <= max_size {
1092            self.host.inject(&self.address, &content);
1093        } else {
1094            for fragment in
1095                fragment::fragment(max_size, version, self.details.tag, self.receiver, &content)
1096                    .into_iter()
1097                    .map(|f| OTREncoder::new().write_encodable(&f).to_vec())
1098            {
1099                self.host.inject(&self.address, &fragment);
1100            }
1101        }
1102    }
1103}
1104
1105/// `AccountDetails` contains our own, static details for an account shared among instances.
1106struct AccountDetails {
1107    policy: Policy,
1108    // TODO tag is duplicate with tag in client profile.
1109    tag: InstanceTag,
1110    account: Vec<u8>,
1111}
1112
1113#[allow(clippy::trivially_copy_pass_by_ref)]
1114fn select_version(policy: &Policy, versions: &[Version]) -> Option<Version> {
1115    if versions.contains(&Version::V4) && policy.contains(Policy::ALLOW_V4) {
1116        Some(Version::V4)
1117    } else if versions.contains(&Version::V3) && policy.contains(Policy::ALLOW_V3) {
1118        Some(Version::V3)
1119    } else {
1120        None
1121    }
1122}
1123
1124#[allow(clippy::trivially_copy_pass_by_ref)]
1125fn filter_versions(policy: &Policy, versions: &[Version]) -> Vec<Version> {
1126    if versions.contains(&Version::V4) && policy.contains(Policy::ALLOW_V4) {
1127        vec![Version::V4]
1128    } else if versions.contains(&Version::V3) && policy.contains(Policy::ALLOW_V3) {
1129        vec![Version::V3]
1130    } else {
1131        Vec::new()
1132    }
1133}
1134
1135#[allow(clippy::too_many_lines)]
1136#[cfg(test)]
1137mod tests {
1138
1139    use std::{cell::RefCell, collections::VecDeque, rc::Rc};
1140
1141    use crate::{
1142        crypto::{dsa, ed448},
1143        instancetag::INSTANCE_ZERO,
1144        Host, OTRError, Policy, ProtocolStatus, UserMessage,
1145    };
1146
1147    use super::{Account, Session};
1148
1149    #[allow(clippy::let_underscore_untyped)]
1150    fn init() {
1151        let _ = env_logger::builder()
1152            .is_test(true)
1153            .filter_level(log::LevelFilter::Trace)
1154            .try_init();
1155    }
1156
1157    // TODO test to prove: multiple simultaneous instances
1158    // TODO test with receiver tag in DH-Commit message. This will likely fail due to AKEContext transfer from instance 0.
1159
1160    #[test]
1161    fn test_plaintext_conversation() {
1162        init();
1163        // Communicate in plaintext with the OTR logic being involved. This demonstrates that
1164        // plaintext messages can be sent regardless.
1165        let keypair_alice = dsa::Keypair::generate();
1166        let identity_alice = ed448::EdDSAKeyPair::generate();
1167        let forging_alice = ed448::EdDSAKeyPair::generate();
1168        let mut messages_alice: Rc<RefCell<VecDeque<Vec<u8>>>> =
1169            Rc::new(RefCell::new(VecDeque::new()));
1170
1171        let keypair_bob = dsa::Keypair::generate();
1172        let identity_bob = ed448::EdDSAKeyPair::generate();
1173        let forging_bob = ed448::EdDSAKeyPair::generate();
1174        let mut messages_bob: Rc<RefCell<VecDeque<Vec<u8>>>> =
1175            Rc::new(RefCell::new(VecDeque::new()));
1176
1177        let host_alice: Rc<dyn Host> = Rc::new(TestHost(
1178            Rc::clone(&messages_bob),
1179            keypair_alice,
1180            usize::MAX,
1181            identity_alice,
1182            forging_alice,
1183            RefCell::new(Vec::new()),
1184        ));
1185        let mut account_alice =
1186            Account::new(Vec::from("alice"), Policy::ALLOW_V3, Rc::clone(&host_alice)).unwrap();
1187        let alice = account_alice.session(b"bob");
1188        let host_bob: Rc<dyn Host> = Rc::new(TestHost(
1189            Rc::clone(&messages_alice),
1190            keypair_bob,
1191            usize::MAX,
1192            identity_bob,
1193            forging_bob,
1194            RefCell::new(Vec::new()),
1195        ));
1196        let mut account_bob =
1197            Account::new(Vec::from("bob"), Policy::ALLOW_V3, Rc::clone(&host_bob)).unwrap();
1198        let bob = account_bob.session(b"alice");
1199
1200        messages_bob
1201            .borrow_mut()
1202            .extend(alice.send(INSTANCE_ZERO, b"Hello bob!").unwrap());
1203        handle_messages("Bob", &mut messages_bob, bob);
1204        messages_alice
1205            .borrow_mut()
1206            .extend(bob.send(INSTANCE_ZERO, b"Hello Alice!").unwrap());
1207        handle_messages("Alice", &mut messages_alice, alice);
1208    }
1209
1210    #[test]
1211    fn test_my_first_otr_session() {
1212        init();
1213        // Verify that an OTR encrypted session can be established. Send messages to ensure
1214        // communication is possible over this confidential session. One side ends the session while
1215        // the other one continues communicating, to ensure that messages do not unintentionally
1216        // pass through unencrypted. Finally, finalize the session on the other side to end up with
1217        // two plaintext sessions, the same as we started with.
1218        let keypair_alice = dsa::Keypair::generate();
1219        let identity_alice = ed448::EdDSAKeyPair::generate();
1220        let forging_alice = ed448::EdDSAKeyPair::generate();
1221        let mut messages_alice: Rc<RefCell<VecDeque<Vec<u8>>>> =
1222            Rc::new(RefCell::new(VecDeque::new()));
1223
1224        let keypair_bob = dsa::Keypair::generate();
1225        let identity_bob = ed448::EdDSAKeyPair::generate();
1226        let forging_bob = ed448::EdDSAKeyPair::generate();
1227        let mut messages_bob: Rc<RefCell<VecDeque<Vec<u8>>>> =
1228            Rc::new(RefCell::new(VecDeque::new()));
1229
1230        let host_alice: Rc<dyn Host> = Rc::new(TestHost(
1231            Rc::clone(&messages_bob),
1232            keypair_alice,
1233            usize::MAX,
1234            identity_alice,
1235            forging_alice,
1236            RefCell::new(Vec::new()),
1237        ));
1238        let mut account_alice =
1239            Account::new(Vec::from("alice"), Policy::ALLOW_V3, Rc::clone(&host_alice)).unwrap();
1240        let alice = account_alice.session(b"bob");
1241        let host_bob: Rc<dyn Host> = Rc::new(TestHost(
1242            Rc::clone(&messages_alice),
1243            keypair_bob,
1244            usize::MAX,
1245            identity_bob,
1246            forging_bob,
1247            RefCell::new(Vec::new()),
1248        ));
1249        let mut account_bob =
1250            Account::new(Vec::from("bob"), Policy::ALLOW_V3, Rc::clone(&host_bob)).unwrap();
1251        let bob = account_bob.session(b"alice");
1252
1253        alice.query().unwrap();
1254        assert!(handle_messages("Alice", &mut messages_alice, alice).is_none());
1255        assert!(handle_messages("Bob", &mut messages_bob, bob).is_none());
1256        assert!(handle_messages("Alice", &mut messages_alice, alice).is_none());
1257        assert!(handle_messages("Bob", &mut messages_bob, bob).is_none());
1258        let result = handle_messages("Alice", &mut messages_alice, alice).unwrap();
1259        let UserMessage::ConfidentialSessionStarted(tag_bob) = result else {
1260            panic!("BUG: expected confidential session to have started now.")
1261        };
1262        assert_eq!(Some(ProtocolStatus::Encrypted), alice.status(tag_bob));
1263        messages_bob.borrow_mut().extend(
1264            alice
1265                .send(tag_bob, b"Hello Bob! Are we chatting confidentially now?")
1266                .unwrap(),
1267        );
1268        let result = handle_messages("Bob", &mut messages_bob, bob).unwrap();
1269        let UserMessage::ConfidentialSessionStarted(tag_alice) = result else {
1270            panic!("BUG: expected confidential session to have started now.")
1271        };
1272        assert_eq!(Some(ProtocolStatus::Encrypted), bob.status(tag_alice));
1273        assert!(matches!(
1274            handle_messages("Bob", &mut messages_bob, bob),
1275            Some(UserMessage::Confidential(_, _, _))
1276        ));
1277        messages_alice
1278            .borrow_mut()
1279            .extend(bob.send(tag_alice, b"Hi Alice! I think we are!").unwrap());
1280        messages_alice
1281            .borrow_mut()
1282            .extend(bob.send(tag_alice, b"KTHXBYE!").unwrap());
1283        assert!(matches!(
1284            bob.end(tag_alice),
1285            Ok(UserMessage::Reset(tag)) if tag == tag_alice
1286        ));
1287        assert!(matches!(
1288            bob.status(tag_alice),
1289            Some(ProtocolStatus::Plaintext)
1290        ));
1291        assert!(matches!(
1292            handle_messages("Alice", &mut messages_alice, alice),
1293            Some(UserMessage::Confidential(_, _, _))
1294        ));
1295        assert!(matches!(
1296            handle_messages("Alice", &mut messages_alice, alice),
1297            Some(UserMessage::Confidential(_, _, _))
1298        ));
1299        assert!(matches!(
1300            handle_messages("Alice", &mut messages_alice, alice),
1301            Some(UserMessage::ConfidentialSessionFinished(_, _))
1302        ));
1303        assert_eq!(Some(ProtocolStatus::Finished), alice.status(tag_bob));
1304        assert!(matches!(
1305            alice.send(tag_bob, b"Hey, wait up!!!"),
1306            Err(OTRError::IncorrectState(_))
1307        ));
1308        assert!(matches!(
1309            alice.end(tag_bob),
1310            Ok(UserMessage::Reset(tag)) if tag == tag_bob
1311        ));
1312        assert!(matches!(
1313            alice.status(tag_bob),
1314            Some(ProtocolStatus::Plaintext)
1315        ));
1316        assert!(messages_bob.borrow().is_empty());
1317        assert!(messages_alice.borrow().is_empty());
1318    }
1319
1320    #[test]
1321    fn test_my_first_otr4_session() {
1322        init();
1323        // Verify that an OTR-encrypted session can be established. Send messages to ensure
1324        // communication is possible over this confidential session. One side ends the session while
1325        // the other one continues communicating, to ensure that messages do not unintentionally
1326        // pass through unencrypted. Finally, finalize the session on the other side to end up with
1327        // two plaintext sessions, the same as we started with.
1328        let mut messages_alice: Rc<RefCell<VecDeque<Vec<u8>>>> =
1329            Rc::new(RefCell::new(VecDeque::new()));
1330        let mut messages_bob: Rc<RefCell<VecDeque<Vec<u8>>>> =
1331            Rc::new(RefCell::new(VecDeque::new()));
1332        let mut account_alice = Account::new(
1333            Vec::from("alice"),
1334            Policy::ALLOW_V4,
1335            Rc::new(TestHost(
1336                Rc::clone(&messages_bob),
1337                dsa::Keypair::generate(),
1338                usize::MAX,
1339                ed448::EdDSAKeyPair::generate(),
1340                ed448::EdDSAKeyPair::generate(),
1341                RefCell::new(Vec::new()),
1342            )),
1343        )
1344        .unwrap();
1345        let alice = account_alice.session(b"bob");
1346        let mut account_bob = Account::new(
1347            Vec::from("bob"),
1348            Policy::ALLOW_V4,
1349            Rc::new(TestHost(
1350                Rc::clone(&messages_alice),
1351                dsa::Keypair::generate(),
1352                usize::MAX,
1353                ed448::EdDSAKeyPair::generate(),
1354                ed448::EdDSAKeyPair::generate(),
1355                RefCell::new(Vec::new()),
1356            )),
1357        )
1358        .unwrap();
1359        let bob = account_bob.session(b"alice");
1360
1361        alice.query().unwrap();
1362        assert!(handle_messages("Alice", &mut messages_alice, alice).is_none());
1363        assert!(handle_messages("Bob", &mut messages_bob, bob).is_none());
1364        assert!(handle_messages("Alice", &mut messages_alice, alice).is_none());
1365        let result = handle_messages("Bob", &mut messages_bob, bob);
1366        let Some(UserMessage::ConfidentialSessionStarted(tag_alice)) = result else {
1367            panic!("BUG: expected confidential session to have started now.")
1368        };
1369        assert_eq!(Some(ProtocolStatus::Encrypted), bob.status(tag_alice));
1370        let result = handle_messages("Alice", &mut messages_alice, alice).unwrap();
1371        let UserMessage::ConfidentialSessionStarted(tag_bob) = result else {
1372            panic!("BUG: expected confidential session to have started now.")
1373        };
1374        assert_eq!(Some(ProtocolStatus::Encrypted), alice.status(tag_bob));
1375        messages_bob.borrow_mut().extend(
1376            alice
1377                .send(tag_bob, b"Hello Bob! Are we chatting confidentially now?")
1378                .unwrap(),
1379        );
1380        assert!(matches!(
1381            handle_messages("Bob", &mut messages_bob, bob),
1382            Some(UserMessage::Confidential(_, _, _))
1383        ));
1384        messages_alice
1385            .borrow_mut()
1386            .extend(bob.send(tag_alice, b"Hi Alice! I think we are!").unwrap());
1387        messages_alice
1388            .borrow_mut()
1389            .extend(bob.send(tag_alice, b"KTHXBYE!").unwrap());
1390        assert!(matches!(
1391            bob.end(tag_alice),
1392            Ok(UserMessage::Reset(tag)) if tag == tag_alice
1393        ));
1394        assert!(matches!(
1395            bob.status(tag_alice),
1396            Some(ProtocolStatus::Plaintext)
1397        ));
1398        assert!(matches!(
1399            handle_messages("Alice", &mut messages_alice, alice),
1400            Some(UserMessage::Confidential(_, _, _))
1401        ));
1402        assert!(matches!(
1403            handle_messages("Alice", &mut messages_alice, alice),
1404            Some(UserMessage::Confidential(_, _, _))
1405        ));
1406        assert!(matches!(
1407            handle_messages("Alice", &mut messages_alice, alice),
1408            Some(UserMessage::ConfidentialSessionFinished(_, _))
1409        ));
1410        assert_eq!(Some(ProtocolStatus::Finished), alice.status(tag_bob));
1411        assert!(matches!(
1412            alice.send(tag_bob, b"Hey, wait up!!!"),
1413            Err(OTRError::IncorrectState(_))
1414        ));
1415        assert!(matches!(
1416            alice.end(tag_bob),
1417            Ok(UserMessage::Reset(tag)) if tag == tag_bob
1418        ));
1419        assert!(matches!(
1420            alice.status(tag_bob),
1421            Some(ProtocolStatus::Plaintext)
1422        ));
1423        assert!(messages_bob.borrow().is_empty());
1424        assert!(messages_alice.borrow().is_empty());
1425    }
1426
1427    #[test]
1428    fn test_fragmented_otr_session() {
1429        init();
1430        // Verify that an OTR encrypted session can be established, even with need for
1431        // fragmentation. Maximum message sizes allowed for communication are specific for each side
1432        // to ensure that difference caused by length of user name, nickname, etc. are allowed.
1433        // Send messages to ensure communication is possible over this confidential session. One
1434        // side ends the session while the other one continues communicating, to ensure that
1435        // messages do not unintentionally pass through unencrypted. Finally, finalize the session
1436        // on the other side to end up with two plaintext sessions, the same as we started with.
1437        let mut messages_alice: Rc<RefCell<VecDeque<Vec<u8>>>> =
1438            Rc::new(RefCell::new(VecDeque::new()));
1439        let mut messages_bob: Rc<RefCell<VecDeque<Vec<u8>>>> =
1440            Rc::new(RefCell::new(VecDeque::new()));
1441        let mut account_alice = Account::new(
1442            Vec::from("alice"),
1443            Policy::ALLOW_V3,
1444            Rc::new(TestHost(
1445                Rc::clone(&messages_bob),
1446                dsa::Keypair::generate(),
1447                50,
1448                ed448::EdDSAKeyPair::generate(),
1449                ed448::EdDSAKeyPair::generate(),
1450                RefCell::new(Vec::new()),
1451            )),
1452        )
1453        .unwrap();
1454        let alice = account_alice.session(b"bob");
1455        let mut account_bob = Account::new(
1456            Vec::from("bob"),
1457            Policy::ALLOW_V3,
1458            Rc::new(TestHost(
1459                Rc::clone(&messages_alice),
1460                dsa::Keypair::generate(),
1461                55,
1462                ed448::EdDSAKeyPair::generate(),
1463                ed448::EdDSAKeyPair::generate(),
1464                RefCell::new(Vec::new()),
1465            )),
1466        )
1467        .unwrap();
1468        let bob = account_bob.session(b"alice");
1469
1470        alice.query().unwrap();
1471        assert!(handle_messages("Alice", &mut messages_alice, alice).is_none());
1472        assert!(handle_messages("Bob", &mut messages_bob, bob).is_none());
1473        assert!(handle_messages("Alice", &mut messages_alice, alice).is_none());
1474        assert!(handle_messages("Bob", &mut messages_bob, bob).is_none());
1475        let result = handle_messages("Alice", &mut messages_alice, alice).unwrap();
1476        let UserMessage::ConfidentialSessionStarted(tag_bob) = result else {
1477            panic!("BUG: expected confidential session to have started now.")
1478        };
1479        assert_eq!(Some(ProtocolStatus::Encrypted), alice.status(tag_bob));
1480        messages_bob.borrow_mut().extend(
1481            alice
1482                .send(tag_bob, b"Hello Bob! Are we chatting confidentially now?")
1483                .unwrap(),
1484        );
1485        let result = handle_messages("Bob", &mut messages_bob, bob).unwrap();
1486        let UserMessage::ConfidentialSessionStarted(tag_alice) = result else {
1487            panic!("BUG: expected confidential session to have started now.")
1488        };
1489        assert_eq!(Some(ProtocolStatus::Encrypted), bob.status(tag_alice));
1490        assert!(matches!(
1491            handle_messages("Bob", &mut messages_bob, bob),
1492            Some(UserMessage::Confidential(_, _, _))
1493        ));
1494        messages_alice
1495            .borrow_mut()
1496            .extend(bob.send(tag_alice, b"Hi Alice! I think we are!").unwrap());
1497        messages_alice
1498            .borrow_mut()
1499            .extend(bob.send(tag_alice, b"KTHXBYE!").unwrap());
1500        assert!(matches!(
1501            bob.end(tag_alice),
1502            Ok(UserMessage::Reset(tag)) if tag == tag_alice
1503        ));
1504        assert!(matches!(
1505            bob.status(tag_alice),
1506            Some(ProtocolStatus::Plaintext)
1507        ));
1508        assert!(matches!(
1509            handle_messages("Alice", &mut messages_alice, alice),
1510            Some(UserMessage::Confidential(_, _, _))
1511        ));
1512        assert!(matches!(
1513            handle_messages("Alice", &mut messages_alice, alice),
1514            Some(UserMessage::Confidential(_, _, _))
1515        ));
1516        assert!(matches!(
1517            handle_messages("Alice", &mut messages_alice, alice),
1518            Some(UserMessage::ConfidentialSessionFinished(_, _))
1519        ));
1520        assert_eq!(Some(ProtocolStatus::Finished), alice.status(tag_bob));
1521        assert!(matches!(
1522            alice.send(tag_bob, b"Hey, wait up!!!"),
1523            Err(OTRError::IncorrectState(_))
1524        ));
1525        assert!(matches!(
1526            alice.end(tag_bob),
1527            Ok(UserMessage::Reset(tag)) if tag == tag_bob
1528        ));
1529        assert!(matches!(
1530            alice.status(tag_bob),
1531            Some(ProtocolStatus::Plaintext)
1532        ));
1533        assert!(messages_bob.borrow().is_empty());
1534        assert!(messages_alice.borrow().is_empty());
1535    }
1536
1537    struct TestHost(
1538        Rc<RefCell<VecDeque<Vec<u8>>>>,
1539        dsa::Keypair,
1540        usize,
1541        ed448::EdDSAKeyPair,
1542        ed448::EdDSAKeyPair,
1543        RefCell<Vec<u8>>,
1544    );
1545
1546    impl Host for TestHost {
1547        fn message_size(&self) -> usize {
1548            self.2
1549        }
1550
1551        fn inject(&self, _address: &[u8], message: &[u8]) {
1552            self.0.borrow_mut().push_back(Vec::from(message));
1553        }
1554
1555        fn keypair(&self) -> Option<&dsa::Keypair> {
1556            Some(&self.1)
1557        }
1558
1559        fn keypair_identity(&self) -> &crate::crypto::ed448::EdDSAKeyPair {
1560            &self.3
1561        }
1562
1563        fn keypair_forging(&self) -> &crate::crypto::ed448::EdDSAKeyPair {
1564            &self.4
1565        }
1566
1567        fn query_smp_secret(&self, _question: &[u8]) -> Option<Vec<u8>> {
1568            Some(b"Password!".to_vec())
1569        }
1570
1571        fn client_profile(&self) -> Vec<u8> {
1572            self.5.borrow().clone()
1573        }
1574
1575        fn update_client_profile(&self, encoded_payload: Vec<u8>) {
1576            self.5.replace(encoded_payload);
1577        }
1578    }
1579
1580    fn handle_messages(
1581        id: &str,
1582        channel: &mut Rc<RefCell<VecDeque<Vec<u8>>>>,
1583        session: &mut Session,
1584    ) -> Option<UserMessage> {
1585        println!("Messages available: {}", channel.borrow_mut().len());
1586        while let Some(m) = channel.borrow_mut().pop_front() {
1587            println!(
1588                "{}: processing message `{}`",
1589                id,
1590                core::str::from_utf8(&m).unwrap()
1591            );
1592            let message = session.receive(&m).unwrap();
1593            extract_readable(id, &message);
1594            if let UserMessage::None = message {
1595                // nothing worthwhile, continue with possible next message
1596            } else {
1597                return Some(message);
1598            }
1599        }
1600        None
1601    }
1602
1603    fn extract_readable(id: &str, msg: &UserMessage) {
1604        match msg {
1605            UserMessage::None => println!("{id}: (none)"),
1606            UserMessage::Plaintext(msg) => {
1607                println!("{id}: {}", core::str::from_utf8(msg).unwrap());
1608            }
1609            UserMessage::ConfidentialSessionStarted(tag) => {
1610                println!("{id}: confidential session started for instance {tag}");
1611            }
1612            UserMessage::Confidential(tag, message, tlvs) => println!(
1613                "{id}: confidential message on {tag}: {} (TLVs: {tlvs:?})",
1614                core::str::from_utf8(message).unwrap(),
1615            ),
1616            UserMessage::ConfidentialSessionFinished(tag, content) => {
1617                println!(
1618                    "{id}: confidential session finished for instance {tag} (\"{}\")",
1619                    core::str::from_utf8(content).unwrap()
1620                );
1621            }
1622            UserMessage::WarningUnencrypted(content) => {
1623                println!(
1624                    "{id} WARNING: unencrypted message: {})",
1625                    core::str::from_utf8(content).unwrap()
1626                );
1627            }
1628            msg => todo!(
1629                "{}: [test utils: extract_readable]: To be implemented: {:?}",
1630                id,
1631                msg
1632            ),
1633        }
1634    }
1635}