matrix_sdk_crypto/verification/sas/
inner_sas.rs

1// Copyright 2020 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::sync::Arc;
16
17use as_variant::as_variant;
18#[cfg(test)]
19use ruma::time::Instant;
20use ruma::{
21    events::key::verification::{cancel::CancelCode, ShortAuthenticationString},
22    TransactionId, UserId,
23};
24use tracing::trace;
25
26use super::{
27    sas_state::{
28        Accepted, Confirmed, Created, Done, KeyReceived, KeySent, KeysExchanged, MacReceived,
29        SasState, Started, WaitingForDone, WeAccepted,
30    },
31    FlowId,
32};
33use crate::{
34    identities::{DeviceData, UserIdentityData},
35    olm::StaticAccountData,
36    verification::{
37        cache::RequestInfo,
38        event_enums::{AnyVerificationContent, OutgoingContent, OwnedAcceptContent, StartContent},
39        Cancelled, Emoji,
40    },
41    OwnUserIdentityData,
42};
43
44#[derive(Clone, Debug)]
45pub enum InnerSas {
46    Created(SasState<Created>),
47    Started(SasState<Started>),
48    Accepted(SasState<Accepted>),
49    WeAccepted(SasState<WeAccepted>),
50    KeyReceived(SasState<KeyReceived>),
51    KeySent(SasState<KeySent>),
52    KeysExchanged(SasState<KeysExchanged>),
53    Confirmed(SasState<Confirmed>),
54    MacReceived(SasState<MacReceived>),
55    WaitingForDone(SasState<WaitingForDone>),
56    Done(SasState<Done>),
57    Cancelled(SasState<Cancelled>),
58}
59
60impl InnerSas {
61    pub fn start(
62        account: StaticAccountData,
63        other_device: DeviceData,
64        own_identity: Option<OwnUserIdentityData>,
65        other_identity: Option<UserIdentityData>,
66        transaction_id: FlowId,
67        started_from_request: bool,
68        short_auth_string: Option<Vec<ShortAuthenticationString>>,
69    ) -> (InnerSas, OutgoingContent) {
70        let sas = SasState::<Created>::new(
71            account,
72            other_device,
73            own_identity,
74            other_identity,
75            transaction_id,
76            started_from_request,
77            short_auth_string,
78        );
79        let content = sas.as_content();
80        (InnerSas::Created(sas), content.into())
81    }
82
83    pub fn started_from_request(&self) -> bool {
84        match self {
85            InnerSas::Created(s) => s.started_from_request,
86            InnerSas::Started(s) => s.started_from_request,
87            InnerSas::WeAccepted(s) => s.started_from_request,
88            InnerSas::Accepted(s) => s.started_from_request,
89            InnerSas::KeyReceived(s) => s.started_from_request,
90            InnerSas::KeySent(s) => s.started_from_request,
91            InnerSas::KeysExchanged(s) => s.started_from_request,
92            InnerSas::Confirmed(s) => s.started_from_request,
93            InnerSas::MacReceived(s) => s.started_from_request,
94            InnerSas::WaitingForDone(s) => s.started_from_request,
95            InnerSas::Done(s) => s.started_from_request,
96            InnerSas::Cancelled(s) => s.started_from_request,
97        }
98    }
99
100    pub fn has_been_accepted(&self) -> bool {
101        match self {
102            InnerSas::Created(_) | InnerSas::Started(_) | InnerSas::Cancelled(_) => false,
103            InnerSas::Accepted(_)
104            | InnerSas::WeAccepted(_)
105            | InnerSas::KeyReceived(_)
106            | InnerSas::KeySent(_)
107            | InnerSas::KeysExchanged(_)
108            | InnerSas::Confirmed(_)
109            | InnerSas::MacReceived(_)
110            | InnerSas::WaitingForDone(_)
111            | InnerSas::Done(_) => true,
112        }
113    }
114
115    pub fn supports_emoji(&self) -> bool {
116        match self {
117            InnerSas::Created(_) => false,
118            InnerSas::Started(s) => s
119                .state
120                .accepted_protocols
121                .short_auth_string
122                .contains(&ShortAuthenticationString::Emoji),
123            InnerSas::WeAccepted(s) => s
124                .state
125                .accepted_protocols
126                .short_auth_string
127                .contains(&ShortAuthenticationString::Emoji),
128            InnerSas::Accepted(s) => s
129                .state
130                .accepted_protocols
131                .short_auth_string
132                .contains(&ShortAuthenticationString::Emoji),
133            InnerSas::KeyReceived(s) => s
134                .state
135                .accepted_protocols
136                .short_auth_string
137                .contains(&ShortAuthenticationString::Emoji),
138            InnerSas::KeySent(s) => s
139                .state
140                .accepted_protocols
141                .short_auth_string
142                .contains(&ShortAuthenticationString::Emoji),
143            InnerSas::KeysExchanged(s) => s
144                .state
145                .accepted_protocols
146                .short_auth_string
147                .contains(&ShortAuthenticationString::Emoji),
148            InnerSas::Confirmed(_) => false,
149            InnerSas::MacReceived(s) => s
150                .state
151                .accepted_protocols
152                .short_auth_string
153                .contains(&ShortAuthenticationString::Emoji),
154            InnerSas::WaitingForDone(_) => false,
155            InnerSas::Done(_) => false,
156            InnerSas::Cancelled(_) => false,
157        }
158    }
159
160    pub fn from_start_event(
161        account: StaticAccountData,
162        other_device: DeviceData,
163        flow_id: FlowId,
164        content: &StartContent<'_>,
165        own_identity: Option<OwnUserIdentityData>,
166        other_identity: Option<UserIdentityData>,
167        started_from_request: bool,
168    ) -> Result<InnerSas, OutgoingContent> {
169        match SasState::<Started>::from_start_event(
170            account,
171            other_device,
172            own_identity,
173            other_identity,
174            flow_id,
175            content,
176            started_from_request,
177        ) {
178            Ok(s) => Ok(InnerSas::Started(s)),
179            Err(s) => Err(s.as_content()),
180        }
181    }
182
183    pub fn accept(
184        self,
185        methods: Vec<ShortAuthenticationString>,
186    ) -> Option<(InnerSas, OwnedAcceptContent)> {
187        let InnerSas::Started(s) = self else { return None };
188        let sas = s.into_we_accepted(methods);
189        let content = sas.as_content();
190
191        trace!(
192            flow_id = sas.verification_flow_id.as_str(),
193            accepted_protocols = ?sas.state.accepted_protocols,
194            "Accepted a SAS verification"
195        );
196
197        Some((InnerSas::WeAccepted(sas), content))
198    }
199
200    #[cfg(test)]
201    #[allow(dead_code)]
202    pub fn set_creation_time(&mut self, time: Instant) {
203        match self {
204            InnerSas::Created(s) => s.set_creation_time(time),
205            InnerSas::Started(s) => s.set_creation_time(time),
206            InnerSas::Cancelled(s) => s.set_creation_time(time),
207            InnerSas::Accepted(s) => s.set_creation_time(time),
208            InnerSas::KeyReceived(s) => s.set_creation_time(time),
209            InnerSas::KeySent(s) => s.set_creation_time(time),
210            InnerSas::KeysExchanged(s) => s.set_creation_time(time),
211            InnerSas::Confirmed(s) => s.set_creation_time(time),
212            InnerSas::MacReceived(s) => s.set_creation_time(time),
213            InnerSas::Done(s) => s.set_creation_time(time),
214            InnerSas::WaitingForDone(s) => s.set_creation_time(time),
215            InnerSas::WeAccepted(s) => s.set_creation_time(time),
216        }
217    }
218
219    pub fn cancel(
220        self,
221        cancelled_by_us: bool,
222        code: CancelCode,
223    ) -> (InnerSas, Option<OutgoingContent>) {
224        let sas = match self {
225            InnerSas::Created(s) => s.cancel(cancelled_by_us, code),
226            InnerSas::Started(s) => s.cancel(cancelled_by_us, code),
227            InnerSas::Accepted(s) => s.cancel(cancelled_by_us, code),
228            InnerSas::WeAccepted(s) => s.cancel(cancelled_by_us, code),
229            InnerSas::KeyReceived(s) => s.cancel(cancelled_by_us, code),
230            InnerSas::KeySent(s) => s.cancel(cancelled_by_us, code),
231            InnerSas::KeysExchanged(s) => s.cancel(cancelled_by_us, code),
232            InnerSas::MacReceived(s) => s.cancel(cancelled_by_us, code),
233            InnerSas::Confirmed(s) => s.cancel(cancelled_by_us, code),
234            InnerSas::WaitingForDone(s) => s.cancel(cancelled_by_us, code),
235            InnerSas::Done(_) | InnerSas::Cancelled(_) => return (self, None),
236        };
237
238        let content = sas.as_content();
239
240        (InnerSas::Cancelled(sas), Some(content))
241    }
242
243    pub fn confirm(self) -> (InnerSas, Vec<OutgoingContent>) {
244        match self {
245            InnerSas::KeysExchanged(s) => {
246                let sas = s.confirm();
247                let content = sas.as_content();
248                (InnerSas::Confirmed(sas), vec![content])
249            }
250            InnerSas::MacReceived(s) => {
251                if s.started_from_request {
252                    let sas = s.confirm_and_wait_for_done();
253                    let contents = vec![sas.as_content(), sas.done_content()];
254
255                    (InnerSas::WaitingForDone(sas), contents)
256                } else {
257                    let sas = s.confirm();
258                    let content = sas.as_content();
259
260                    (InnerSas::Done(sas), vec![content])
261                }
262            }
263            _ => (self, Vec::new()),
264        }
265    }
266
267    pub fn receive_any_event(
268        self,
269        sender: &UserId,
270        content: &AnyVerificationContent<'_>,
271    ) -> (Self, Option<(OutgoingContent, Option<RequestInfo>)>) {
272        match content {
273            AnyVerificationContent::Accept(c) => match self {
274                InnerSas::Created(s) => match s.into_accepted(sender, c) {
275                    Ok(s) => {
276                        let (content, request_info) = s.as_content();
277                        (InnerSas::Accepted(s), Some((content, Some(request_info))))
278                    }
279                    Err(s) => {
280                        let content = s.as_content();
281                        (InnerSas::Cancelled(s), Some((content, None)))
282                    }
283                },
284                InnerSas::Started(s) => match s.into_accepted(sender, c) {
285                    Ok(s) => {
286                        let (content, request_info) = s.as_content();
287                        (InnerSas::Accepted(s), Some((content, Some(request_info))))
288                    }
289                    Err(s) => {
290                        let content = s.as_content();
291                        (InnerSas::Cancelled(s), Some((content, None)))
292                    }
293                },
294                _ => (self, None),
295            },
296            AnyVerificationContent::Cancel(c) => {
297                let (sas, _) = self.cancel(false, c.cancel_code().to_owned());
298                (sas, None)
299            }
300            AnyVerificationContent::Key(c) => match self {
301                InnerSas::Accepted(s) => match s.into_key_received(sender, c) {
302                    Ok(s) => (InnerSas::KeyReceived(s), None),
303                    Err(s) => {
304                        let content = s.as_content();
305                        (InnerSas::Cancelled(s), Some((content, None)))
306                    }
307                },
308                InnerSas::KeySent(s) => match s.into_keys_exchanged(sender, c) {
309                    Ok(s) => (InnerSas::KeysExchanged(s), None),
310                    Err(s) => {
311                        let content = s.as_content();
312                        (InnerSas::Cancelled(s), Some((content, None)))
313                    }
314                },
315                InnerSas::WeAccepted(s) => match s.into_key_received(sender, c) {
316                    Ok(s) => {
317                        let (content, request_info) = s.as_content();
318                        (InnerSas::KeyReceived(s), Some((content, Some(request_info))))
319                    }
320                    Err(s) => {
321                        let content = s.as_content();
322                        (InnerSas::Cancelled(s), Some((content, None)))
323                    }
324                },
325
326                _ => (self, None),
327            },
328            AnyVerificationContent::Mac(c) => match self {
329                InnerSas::KeysExchanged(s) => match s.into_mac_received(sender, c) {
330                    Ok(s) => (InnerSas::MacReceived(s), None),
331                    Err(s) => {
332                        let content = s.as_content();
333                        (InnerSas::Cancelled(s), Some((content, None)))
334                    }
335                },
336                InnerSas::Confirmed(s) =>
337                // TODO remove the else branch when we remove the ability to
338                // start from a `m.key.verification.start` event.
339                {
340                    match if s.started_from_request {
341                        s.into_waiting_for_done(sender, c)
342                            .map(|s| (Some((s.done_content(), None)), InnerSas::WaitingForDone(s)))
343                    } else {
344                        s.into_done(sender, c).map(|s| (None, InnerSas::Done(s)))
345                    } {
346                        Ok((c, s)) => (s, c),
347                        Err(s) => {
348                            let content = s.as_content();
349                            (InnerSas::Cancelled(s), Some((content, None)))
350                        }
351                    }
352                }
353                _ => (self, None),
354            },
355            AnyVerificationContent::Done(c) => match self {
356                InnerSas::WaitingForDone(s) => match s.into_done(sender, c) {
357                    Ok(s) => (InnerSas::Done(s), None),
358                    Err(s) => {
359                        let content = s.as_content();
360                        (InnerSas::Cancelled(s), Some((content, None)))
361                    }
362                },
363                _ => (self, None),
364            },
365            AnyVerificationContent::Request(_)
366            | AnyVerificationContent::Ready(_)
367            | AnyVerificationContent::Start(_) => (self, None),
368        }
369    }
370
371    pub fn mark_request_as_sent(self, request_id: &TransactionId) -> Option<Self> {
372        match self {
373            InnerSas::Accepted(s) => s.into_key_sent(request_id).map(InnerSas::KeySent),
374            InnerSas::KeyReceived(s) => {
375                s.into_keys_exchanged(request_id).map(InnerSas::KeysExchanged)
376            }
377            InnerSas::Created(_)
378            | InnerSas::WeAccepted(_)
379            | InnerSas::Started(_)
380            | InnerSas::KeySent(_)
381            | InnerSas::KeysExchanged(_)
382            | InnerSas::Confirmed(_)
383            | InnerSas::MacReceived(_)
384            | InnerSas::WaitingForDone(_)
385            | InnerSas::Done(_)
386            | InnerSas::Cancelled(_) => Some(self),
387        }
388    }
389
390    pub fn can_be_presented(&self) -> bool {
391        matches!(self, InnerSas::KeysExchanged(_) | InnerSas::MacReceived(_))
392    }
393
394    pub fn is_done(&self) -> bool {
395        matches!(self, InnerSas::Done(_))
396    }
397
398    pub fn is_cancelled(&self) -> bool {
399        matches!(self, InnerSas::Cancelled(_))
400    }
401
402    pub fn have_we_confirmed(&self) -> bool {
403        matches!(self, InnerSas::Confirmed(_) | InnerSas::WaitingForDone(_) | InnerSas::Done(_))
404    }
405
406    pub fn timed_out(&self) -> bool {
407        match self {
408            InnerSas::Created(s) => s.timed_out(),
409            InnerSas::Started(s) => s.timed_out(),
410            InnerSas::Cancelled(s) => s.timed_out(),
411            InnerSas::Accepted(s) => s.timed_out(),
412            InnerSas::KeyReceived(s) => s.timed_out(),
413            InnerSas::KeySent(s) => s.timed_out(),
414            InnerSas::KeysExchanged(s) => s.timed_out(),
415            InnerSas::Confirmed(s) => s.timed_out(),
416            InnerSas::MacReceived(s) => s.timed_out(),
417            InnerSas::WaitingForDone(s) => s.timed_out(),
418            InnerSas::Done(s) => s.timed_out(),
419            InnerSas::WeAccepted(s) => s.timed_out(),
420        }
421    }
422
423    pub fn emoji(&self) -> Option<[Emoji; 7]> {
424        match self {
425            InnerSas::KeysExchanged(s) => Some(s.get_emoji()),
426            InnerSas::MacReceived(s) => Some(s.get_emoji()),
427            _ => None,
428        }
429    }
430
431    pub fn emoji_index(&self) -> Option<[u8; 7]> {
432        match self {
433            InnerSas::KeysExchanged(s) => Some(s.get_emoji_index()),
434            InnerSas::MacReceived(s) => Some(s.get_emoji_index()),
435            _ => None,
436        }
437    }
438
439    pub fn decimals(&self) -> Option<(u16, u16, u16)> {
440        match self {
441            InnerSas::KeysExchanged(s) => Some(s.get_decimal()),
442            InnerSas::MacReceived(s) => Some(s.get_decimal()),
443            _ => None,
444        }
445    }
446
447    pub fn verified_devices(&self) -> Option<Arc<[DeviceData]>> {
448        as_variant!(self, InnerSas::Done).map(|s| s.verified_devices())
449    }
450
451    pub fn verified_identities(&self) -> Option<Arc<[UserIdentityData]>> {
452        as_variant!(self, InnerSas::Done).map(|s| s.verified_identities())
453    }
454}