otrr/
lib.rs

1// SPDX-License-Identifier: LGPL-3.0-only
2
3// NOTES:
4// - keeping score (significant OTRv4 spec errors): 1.) use of secret key material after clearing because receiving messages out of expected order.; 2.) need to process message numbers provisionally because these are early steps leading up to message authentication, therefore cannot be trusted; 3.) merging protocol state machine and DAKE state machine provides opening to Denial-of-Service attacks.
5
6// TODO
7// - risks/limitations of protocol version 3:
8// - does not satisfy recent recommendation for DH moduli of `>= 2048`.
9// - DH modulus in use is likely candidate for precomputation ()
10// - Sources to check:
11// - [WeakDH](<https://weakdh.org/> "Weak Diffie-Hellman and the Logjam Attack")
12// - [LogJam](<https://en.wikipedia.org/wiki/Logjam_(computer_security)>)
13// - [DHEat Attack](<https://dheatattack.com/> "DoS attack that can be performed by enforcing the Diffie-Hellman key exchange")
14// - [RFC 5114](<https://www.rfc-editor.org/rfc/rfc5114.html>)
15
16#![deny(unused_must_use)]
17#![warn(clippy::pedantic)]
18#![allow(
19    dead_code,
20    clippy::unnecessary_unwrap,
21    clippy::module_name_repetitions,
22    clippy::doc_markdown,
23    clippy::needless_range_loop,
24    clippy::for_kv_map
25)]
26
27use ake::AKEError;
28use bitflags::bitflags;
29use crypto::{dsa, ed448, CryptoError};
30use encoding::TLV;
31use instancetag::InstanceTag;
32
33extern crate log;
34
35mod ake;
36mod dake;
37mod encoding;
38mod fragment;
39mod keymanager;
40mod messages;
41mod protocol;
42mod smp;
43mod smp4;
44mod utils;
45
46// TODO evaluate for each `pub mod` members whether to expose outside of crate
47pub mod clientprofile;
48pub mod crypto;
49pub mod instancetag;
50pub mod session;
51
52// TODO initialization-time checking:
53//   1. CPU capabilities: usize 32-bit or 64-bit, given checking for appropriate boundaries throughout code. (e.g. encoding.rs serialization)
54//      - usize >= u32 for array-indexing in OTR-encoding.
55//      - usize >= u32 for array-indexing using KeyID. (protocol.rs)
56// TODO can we use top-level std::panic::catch_unwind for catching/diagnosing unexpected failures? (isolate panics within single session/instance)
57// TODO `encoding#OTR_USE_INFORMATION_MESSAGE`: make accompanying message changeable, consider length for fragmenting.
58// TODO add periodic heartbeat message
59// TODO support messages in backlog for sending when confidential session established?
60// TODO replace once_cell::Lazy with std::lazy::Lazy once the api is in stable.
61// TODO check API guidelines (https://rust-lang.github.io/api-guidelines/checklist.html)
62// TODO consider introducing (generally) logging to keep track of the internal process.
63// TODO currently two different RNG types in use. (See DSA for OsRng)
64// TODO global review of cleaning sensitive memory. (1. can we zeroize BigUint? for SMP, keymanager, etc. There is a cfg(zeroize) for biguint-dig crate, apparently. 2. Review existing uses of Biguint for clearing.)
65// TODO review allow/warn/deny settings per file for clippy et al.
66// TODO store plaintext message for possible retransmission (various states, see spec)
67// TODO was there a requirement that other party's dh key must not be equal to a previous key? If so, would we need to remember more keys?
68// TODO review all zeroing, trait drop::Drop
69// TODO review need for constant-time handling (e.g. comparisons)
70// TODO whitespace-tag is now placed at the beginning of the message. Better location?
71// TODO hunt for variables that could be defined `const`.
72// TODO consider using something like a NonZeroU16 trait for certain datatypes to enforce correct logic.
73// TODO apply BigUint::zeroize for sensitive values
74// TODO crypto: at this point, there is no zeroing of BigInt/BigUint values
75// REMARK clean up asserts that are clearly only used to evalute/confirm (static) control flow logic. (may impact constant-time expectations)
76// REMARK allow defining custom message to be included with the OTR Query-tag.
77// REMARK expose TLV 0 for manual padding by client?
78// REMARK switch from once_cell::lazy::Lazy to std::lazy::Lazy, once it is in rust nightly.
79// REMARK there are two distinct implementations of many cryptography primitives (sha1, signatures) because individual implementations next to `ring`.
80
81/// `UserMessage` represents the resulting Message intended for the messaging client, possibly
82/// containing content relevant to display to the user.
83#[derive(Debug)]
84pub enum UserMessage {
85    /// Nothing received that is relevant to report/transfer back to the messaging client.
86    None,
87    /// Message for user received over open, plaintext transport.
88    Plaintext(Vec<u8>),
89    /// While encrypted sessions are present or the policy requires encryption, a message is
90    /// received in plaintext. The client must know such that it can issue a warning.
91    WarningUnencrypted(Vec<u8>),
92    /// OTR error message received.
93    Error(Vec<u8>),
94    /// Message state reset to "plaintext". (by user action)
95    Reset(InstanceTag),
96    /// Confidential session started, transitioned to "encrypted" state.
97    ConfidentialSessionStarted(InstanceTag),
98    /// Message for user received over confidential OTR transport.
99    Confidential(InstanceTag, Vec<u8>, Vec<TLV>),
100    /// Confidential session ended, transitioned to "finished" state. (Session ended by other
101    /// party.)
102    ConfidentialSessionFinished(InstanceTag, Vec<u8>),
103    /// SMP process succeeded, signaling the client that authenticity is verified.
104    SMPSucceeded(InstanceTag),
105    /// SMP process failed, signaling the client that some final concluion was reached.
106    SMPFailed(InstanceTag),
107}
108
109/// `OTRError` is the enum containing the various errors that can occur.
110#[derive(Debug)]
111pub enum OTRError {
112    /// Message contained invalid data according to the OTR protocol.
113    ProtocolViolation(&'static str),
114    /// Message payload is incomplete. The message cannot be reconstructed from the received bytes.
115    IncompleteMessage,
116    /// Encrypted message is unreadable due to loss of keys and/or wrong protocol state.
117    UnreadableMessage(InstanceTag),
118    /// An OTR message was received that is intended for a different instance (client).
119    MessageForOtherInstance,
120    /// Message to be sent to an unknown instance.
121    UnknownInstance(InstanceTag),
122    /// Unsupported version encountered.
123    UnsupportedVersion(u16),
124    /// Messaging is blocked in OTR protocol "Finished" state to ensure no accidental disclosure occurs.
125    IncorrectState(&'static str),
126    /// Violation of cryptographic or mathematical requirement for correct/secure operation.
127    CryptographicViolation(CryptoError),
128    /// `PolicyRestriction` indicates an error caused by the active policy.
129    PolicyRestriction(&'static str),
130    /// (AKE) `AuthenticationError` indicates that there was an error during AKE.
131    AuthenticationError(AKEError),
132    /// `SMPInProgress` indicates that an SMP exchange is in progress, so to initiate a new SMP,
133    /// the previous one needs to be aborted first.
134    SMPInProgress,
135    /// `SMPSuccess` indicates successful finishing SMP without a follow-up TLV needing to be sent.
136    SMPSuccess(Option<TLV>),
137    /// `SMPFailed` indicates a unsuccessfully completed SMP.
138    SMPFailed(Option<TLV>),
139    /// `SMPAborted` indicates SMP process was aborted, most likely by user request. Provided TLV
140    /// can be sent to other party to signal SMP abort. The boolean value indicates whether the
141    /// abort-action needs to be communicated, that is: true to require sending abort-TLV, false if
142    /// no further action needed.
143    SMPAborted(bool),
144    /// `UserError` indicates a user error, with a description provided in the tuple.
145    UserError(&'static str),
146}
147
148/// `ProtocolStatus` indicates the protocol status: plaintext, encrypted, finished.
149/// If matching for exact protocol state, combine with `Version` to include version-matching.
150#[derive(PartialEq, Eq, Debug)]
151pub enum ProtocolStatus {
152    Plaintext,
153    Encrypted,
154    Finished,
155}
156
157const SUPPORTED_VERSIONS: [Version; 2] = [Version::V3, Version::V4];
158
159/// `Version` contains the various supported OTR protocol versions.
160#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
161pub enum Version {
162    None,
163    // V1 will not be supported.
164    // V2 will not be supported.
165    V3,
166    V4,
167    Unsupported(u16),
168}
169
170bitflags! {
171    /// `Policy` bit-flags can be set to indicate how OTR should respond to certain events related to messaging and the OTR protocol.
172    pub struct Policy: u32 {
173    // ALLOW_V1
174    //     Allow version 1 of the OTR protocol to be used (in general this document will not address OTR protocol version 1; see previous protocol documents for these details).
175    //const ALLOW_V1 = 0b00000001;
176    // ALLOW_V2
177    //     Allow version 2 of the OTR protocol to be used.
178    //const ALLOW_V2 = 0b00000010;
179    // ALLOW_V3
180    //     Allow version 3 of the OTR protocol to be used.
181    const ALLOW_V3 = 0b0000_0000_0000_0100;
182    // REQUIRE_ENCRYPTION
183    //     Refuse to send unencrypted messages.
184    const REQUIRE_ENCRYPTION = 0b0000_0000_0000_1000;
185    // SEND_WHITESPACE_TAG
186    //     Advertise your support of OTR using the whitespace tag.
187    const SEND_WHITESPACE_TAG = 0b0000_0000_0001_0000;
188    // WHITESPACE_START_AKE
189    //     Start the OTR AKE when you receive a whitespace tag.
190    const WHITESPACE_START_AKE = 0b0000_0000_0010_0000;
191    // ERROR_START_AKE
192    //     Start the OTR AKE when you receive an OTR Error Message.
193    const ERROR_START_AKE = 0b0000_0000_0100_0000;
194    const ALLOW_V4 = 0b0000_0001_0000_0000;
195    }
196}
197
198/// `TLV_TYPE` is an alias for an u16 value. The values are not restricted. Therefore define the type.
199pub type TLVType = u16;
200
201#[allow(clippy::upper_case_acronyms)]
202pub type SSID = [u8; 8];
203
204/// Host represents the interface to the host application, for calling back into the messaging
205/// client.
206// TODO consider what it would take to change the API to allow for e.g. failing `inject` calls, meaning that otrr would, for instance, need to abort and ask client to redo the action at a later time. (Probably not worth the effort.)
207pub trait Host {
208    /// `message_size` queries the maximum message size accepted by the underlying transport.
209    ///
210    /// It is expected that smaller message are allowed. The message size will be taken as a strict
211    /// upper bound and it is expected that messages up to exactly that number in size are elligible
212    /// for transport, while even a single byte more may mean -- in the worst case -- that the full
213    /// message is dropped.
214    ///
215    /// `message_size` is called for every message constructed. If name changes, connection changes,
216    /// etc. are determining factors for the maximum message size, then the size only has to be
217    /// stable for a single (OTR-encoded) message to be constructed.
218    fn message_size(&self) -> usize {
219        usize::MAX
220    }
221
222    /// Inject a message into the messaging's transport stream. (I.e. protocol-related so not
223    /// relevant to return to the client.)
224    /// NOTE: `otrr` assumes that injection of the provided message into the transport succeeds.
225    fn inject(&self, account: &[u8], message: &[u8]);
226
227    /// Acquire the long-term (legacy) DSA keypair from the host application. The long-term keypair,
228    /// that is used for authentication purposes, is requested from the host application. This
229    /// allows the host control over which keypair to provide for which account.
230    fn keypair(&self) -> Option<&dsa::Keypair>;
231
232    /// keypair_identity is the OTRv4 long-term (identity) keypair.
233    fn keypair_identity(&self) -> &ed448::EdDSAKeyPair;
234
235    /// keypair_forging is the OTRv4 forging keypair.
236    fn keypair_forging(&self) -> &ed448::EdDSAKeyPair;
237
238    /// `query_smp_secret` triggers a query in the host application (chat client) to ask for the
239    /// secret answer that is necessary to continue the SMP.
240    fn query_smp_secret(&self, question: &[u8]) -> Option<Vec<u8>>;
241
242    /// `client_profile` retrieves the client profile from the host application. An empty vector
243    /// indicates that no client profile exists, therefore one will be generated from internally and
244    /// keypairs will be queried from the host to complete a payload. `update_client_profile` is
245    /// called with a (renewed) client profile payload that should be stored by the host/chat
246    /// application.
247    // TODO callers in DAKE will assume a readily-available profile payload is guaranteed. Is this ensured for all cases?
248    fn client_profile(&self) -> Vec<u8>;
249
250    /// `update_client_profile` sends the host an encoded client profile payload to be stored for
251    /// use and restoring.
252    fn update_client_profile(&self, encoded_payload: Vec<u8>);
253}