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}