Introduction
I needed an OTR library in C# that I could use for instant messaging clients that run on the Windows, Linux and Android platforms but I couldn’t find any. As a result, I decided to roll my own. The attached compressed files are the library and a console application that demonstrates the use of the Library. The OTR protocol and the Library’s interface and event functions are described below. The Library is compatible with OTR clients that implement versions 2 and 3 of the protocol [1], [2]. A client implementing this library successfully established
OTR sessions with the Pidgin [3] and Spark IM [4] clients.
Note that this Library uses the BouncyCastle library for most of its cryptographic functions.
Background
Off-The-Record (OTR) [5] is a protocol that provides security around real-time instant messaging (IM) communications.
It assures the following;
- Confidentiality: Messages are encrypted.
- Authentication: You are who you say you are. And messages sent by you can be verified by your chat partner (buddy) and vice versa.
- Perfect Forward Secrecy: Each instant message sent is encrypted using a different encryption key which is discarded after use. Compromising a single encryption key does not impact on the
confidentiality of other messages sent or those to be sent in the future. In addition, each message is authenticated using a different Message Authentication Code (MAC) key.
- Deniability: The MAC keys that have already been
used and will not be used again are included in outgoing messages. The idea is
that since these keys are in the public domain any one could have created these
keys (including your chat partner) and therefore forged a message.
In addition to the above, the OTR also defines a Socialist Millionaire Protocol (SMP) that could be used to detect a man-in-the-middle attack during an ongoing conversation.
For the SMP process to successfully complete, you and your chat buddy must have a secret that is known to just you and him/her.
The failure of the SMP process is an indication that the encrypted session between you and your chat partner has been hijacked by a third party.
Using the code
Let’s assume that Alice wants to establish an OTR session with her friend, Bob.
In order to do this she has to request an OTR session from Bob and on receipt of this request Bob starts the OTR session proper.
The code below shows how Alice goes about requesting an OTR session.
OTRSessionManager _alice_otr_session_manager = null;
string _my_unique_id = "alice";
string _my_buddy_unique_id = "bob";
_alice_otr_session_manager = new OTRSessionManager(_my_unique_id);
_alice_otr_session_manager.OnOTREvent += new OTREventHandler(OnAliceOTRMangerEventHandler);
_alice_otr_session_manager.CreateOTRSession(_my_buddy_unique_id);
_alice_otr_session_manager.RequestOTRSession(_my_buddy_unique_id, OTRSessionManager.GetSupportedOTRVersionList()[0]);
Observe that the OTR session manager is initialized using your unique ID. As soon as the OTR manager is initialized,
it must be connected to the OTR event handler. See the OTR Event section for a description of these event types.
Each OTR session established for the buddies you are communicating with must be created using the unique ID of that buddy.
Once this is done, references to a session are achieved using this unique ID. In other words, your buddy’s unique ID doubles as
the session ID.
Requesting an OTR session involves calling the RequestOTRSession
function and passing it your buddy’s ID and
the OTR version you wish to use. This Library supports versions 2 and 3 of the OTR protocol.
The versions supported by this library are contained in a string list that can be accessed by calling
the GetSupportedOTRVersionList
static function of the OTRSessionManager
.
OTR uses Digital Signature Algorithm (DSA) public keys as part of the authentication and SMP processes.
If the client already has a DSA key, then the client can pass the DSA public and private key parameters
i.e., P, Q, G and X to the CreateOTRSession
function. The DSA key parameters must be formatted as a DSAKeyParams
object
before it is passed to the function in question. If the client doesn’t have a DSA key, a random one is created by this library.
See the accompanying OTRLibTest
console application for examples on how this is accomplished.
Your current DSA key can be gotten by calling GetSessionDSAHexParams
function.
It returns a DSAKeyParams
object that contains the P, Q, G and X elements of the key.
The OTR specification defines an object called a Finger Print.
The Finger Print is computed using the DSA public key parameters (i.e., P, Q, G and Y) as input.
To retrieve your Finger Print in hexadecimal string call the GetSessionDSAFingerPrint
function.
Similarly, to retrieve the Finger Print of your buddy call the GetMyBuddyFingerPrint
function.
_alice_otr_session_manager.ProcessOTRMessage(_my_buddy_unique_id, otr_message_string);
All OTR session string messages received by a client are passed to the ProcessOTRMessage function for further processing as shown above.
_alice_otr_session_manager.EncryptMessage(_my_buddy_unique_id, message_string);
Messages to be encrypted are passed to the EncryptMessage
function.
If there are no errors in the encryption process then an OTR SEND event is triggered.
See OTR Event section below for details of this event.
There are four more EncryptMessage
functions that accept arguments that either allow for (1)
the padding of encrypted data to hide the length of the message or
(2) allow for indirect initiation of the SMP process or both (1) and (2) simultaneously.
An associated function is the EncryptFragments
function that allows for the encryption and fragmentation
of large messages before transmission to a buddy. The buddy should be able to reassemble these message fragments
and present the whole message to the user’s client.
OTR Events
There are 9 main events and 4 sub-events associated with the OTR manager.
Note that the OTR manager can differentiate between events fired by multiple OTR sessions by simply reading the event’s GetSessionID
function. The session ID string will correspond to the my_buddy_unique_id
string passed to the CreateSession
function.
These events are described below;
ERROR Event: Such an event indicates that an error has occurred
within an OTR session. This event can be fatal or benign. Once notified of an
event that may appear fatal it is up to the IM interface to close the OTR
session. An example of a fatal event is when a client is unable to verify the
DSA public key of a buddy. In such an instance, it is advisable to close the
OTR session. Closing the OTR session will notify the buddy of same.
An instance of a non-fatal event is when a message cannot be decrypted. When this happens the client is notified and an
error message is automatically sent to the buddy. Such an event does not require closing the OTR session.
When this event fires, the error messages can be read by calling the event’s GetErrorMessage
and GetErrorVerbose
functions.
Debug Event:This event outputs the messages that give an idea about what is happening internally within an OTR session
e.g., types of messages received and sent. The debug message can be read by calling the event’s GetMessage
function.
Debug events can be enabled by setting the debug variable in the CreateOTRSession
function to true
.
MESSAGE Event: This event signals the successful decryption of a received OTR encrypted message. The decrypted message can be
read by calling the event’s GetMessage
function. Recall from the Background section that old MAC keys were said to be transmitted with outgoing messages. When this event fires,
the old MAC keys (in bytes) can be retrieved by calling the GetOldMacKeys
function of the event. Note that each MAC key is 20 bytes long.
If the GetOldMacKeys
function returns 40 bytes then you know there are two MAC keys contained within.
EXTRA_KEY_REQUEST Event: This event indicates that a buddy wants you both to use the extra AES symmetric for some purpose. The AES
Key bytes can be retrieved by calling the GetExtraSymmetricKey
Session function. You can request the use of the extra AES symmetric key by calling the RequestExtraKeyUse
function . This event is only valid for Version 3 of the OTR protocol.
SEND Event: When an OTR session wants to send an OTR message to a buddy, it fires the SEND event. Data to be sent is
retrieved by calling the event’s GetMessage
function.
READY Event: The READY event signals the successful establishment of an encrypted OTR session between two IM clients. Note that no encrypted
conversational data can be sent before this event fires. An attempt to encrypt and send a message before this event fires will trigger an error event. The current
state of an OTR session can be gotten by calling the GetSessionMessageState
function. If the OTR session is not in the
MSG_STATE_ENCRYPTED
state then it is not ready.
CLOSED Event: This event signifies the closing of an OTR session. This is usually in response to the user calling the
CloseAllSessions
or CloseSession
functions. This event is also triggered by a close session message received
from a buddy.
HEART_BEAT Event: This event indicates that a buddy wants to find out if you are still online and willing to communicate over an
OTR encrypted session. This is usually sent when you’ve been idle for a while. You can respond by sending a message to your buddy or simply send a heartbeat
of your own using the SendHeartBeat
function.
SMP_MESSAGE Event: This event fires, as it were, in response to the completion of an SMP process.
There are four sub-events associated with this event;
SUCCEEDED
: The SMP process completed successfully i.e., the OTR session has not been
hijacked. FAILED
: The SMP process was not successful i.e., the session may have been hijacked. ABORT
: An ongoing SMP process was aborted. SEND
: This event should never be received as it is only used internally within an OTR
session.
To detect the fired SMP sub-event call the GetSMPEvent
function of the main event.
<p>
The SMP process can be initiated directly by calling the <code>StartSMP
function or indirectly by setting the
start_smp
argument of any of the
EncryptMessage
message function to
true
.
The shared user secret input for the SMP process can be set by passing the secret string to the
SetSMPUserSecret
function.
The current shared secret can be retrieved by calling
GetSMPUserSecret
The default shared secret string is "
Kittens are funny". :D
To force OTR to send SMP messages as fragments call the SetSMPFragLength
function to set the maximum length of each fragment. To get the current fragment length for SMP messages call the GetSMPFragLength
function.
To stop an ongoing SMP process call AbortSMP
.
Point of Interest
The SMP process involves crunching of large integers (1536 bits) and as a result it can be slow. This process took an average of 27 seconds to complete when I ran it between a client interface implementing this library and a Pidgin instant messaging client [3] on a Windows 7 platform. This will have implications when attempting to run the SMP on a limited device e.g., smart phones etc. The implemented SMP code is contained in the SMPManager.cs
file. If anyone can improve on this code to make it faster and share this improvement, then that would be appreciated.
The SMP execution speed has now been improved. It now runs under 7 seconds. I realised that I was using 1536 bytes instead of 192 bytes (i.e., 1536 bits) integer exponents stipulated by the recently updated specification [6] . Disregard the point of interest crossed out above :)
Mohamed Mansour has
ported the library for use on the Windows 8/Phone platform.
Find it here.
History
<ul>
<li>28/08/2013: First version. </li>
<li>18/09/2013: Added the <code>SetSMPFragLength
and
GetSMPFragLength
functions to the Library and updated the article to reflect same.
20/09/2013: Improved the runtime of the SMP. See Point of Interest section.10/11/13: Corrected the point at which the extra AES key is
computed in version 3.27/01/2014 : Updated the Point of Interest section with a link to the Windows
8/Phone port of the library.04/04/2014: A Mono build of library added.29/04/2014: Resolved the loss of key synchronization when one party does all the talking.
References
- OTR Version 2
- OTR Version 3
- Pidgin
- Spark
- OTR main page
- Updated Specification
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.