diff options
Diffstat (limited to '')
-rw-r--r-- | comm/third_party/libotr/UPGRADING | 515 |
1 files changed, 515 insertions, 0 deletions
diff --git a/comm/third_party/libotr/UPGRADING b/comm/third_party/libotr/UPGRADING new file mode 100644 index 0000000000..f7445c3472 --- /dev/null +++ b/comm/third_party/libotr/UPGRADING @@ -0,0 +1,515 @@ +Table of Contents + +1. Introduction +2. Major Additions +2.1. Instance Tags +2.2. Asynchronous Private Key Generation +2.3. Extra Symmetric Key +2.4. Convert Operations +2.5. SMP, Error, and Message Event Callbacks +2.6. Fragmentation Changes +3. Required Changes +3.1. OtrlMessageAppOps Callbacks +3.1.1. Removed Operations +3.1.2. Added Operations +3.2. Instance Tags +3.3. Fragmentation Changes +3.4. Asynchronous Private Key Generation +3.5. Library Initialization + +1. Introduction + +This file contains information about the changes between the 3.2.0 and +the 4.0.0 APIs for libotr. Note that applications compiled against +previous versions of OTR will not work with libotr 4.0.0. + +2. Major Additions + +This section describes the new features in OTR 4.0.0 along with a short +history or motivation for each. + +2.1. Instance Tags + +Clients generate instance tags that are intended to be persistent. If +the same client is logged into the same account from multiple locations, +the intention is that he or she will have different instance tags at +each location. OTR wire messages (fragmented and unfragmented) include +the source and destination instance tags. If a client receives a message +that lists a destination instance tag different from his own, the client +will discard it (and issue a callback notifying the application of the +event). + +This avoids an issue on IM networks that always relay all messages to +all sessions of a client who is logged in multiple times. In this +situation, OTR clients can attempt to establish an OTR session +indefinitely if there are interleaving messages from each of the +sessions. + +2.2. Asynchronous Private Key Generation + +Key generation can happen in a separate thread without blocking an +application. + +2.3. Extra Symmetric Key + +An extra symmetric key is kept synchronized during a conversation with a +buddy. Either side can send a signal that they wish to use this key for +some external purpose (e.g. things like a file transfer, in some other +channel of communication). + +2.4. Convert Operations + +There is now a callback that is made immediately before a message is +encrypted and immediately after a message is decrypted. This callback +can tweak the plaintext message as needed. For example, this could allow +an application to convert formatting on a message if this would normally +be done on the plaintext by some other entity while the message is in +transit. + +2.5. SMP, Error, and Message Event Callbacks + +To avoid hard-coded English phrases in libotr, operations which used to +pass back strings are replaced by operations that pass back event codes. + +2.6. Fragmentation Changes + +In libotr version 3.2.0, you would need to call otrl_message_sending() +to create an encrypted message, and then call fragment_and_send() to get +libotr to fragment and inject that message. In libotr 4.0.0, the +functionality of fragment_and_send() has been integrated into +otrl_message_sending(). + +3. Required Changes + +3.1. OtrlMessageAppOps Callbacks + +3.1.1. Removed Operations + +/* Display a notification message for a particular accountname / + * protocol / username conversation. */ +void (*notify)(void *opdata, OtrlNotifyLevel level, + const char *accountname, const char *protocol, + const char *username, const char *title, + const char *primary, const char *secondary); + +The notify() operation was removed since it was used to pass in +hardcoded English strings. This has been replaced by error and message +event callbacks, described below, which pass event codes rather than +hardcoded strings. + + +/* Display an OTR control message for a particular accountname / + * protocol / username conversation. Return 0 if you are able to + * successfully display it. If you return non-0 (or if this + * function is NULL), the control message will be displayed inline, + * as a received message, or else by using the above notify() + * callback. */ +int (*display_otr_message)(void *opdata, const char *accountname, + const char *protocol, const char *username, const char *msg); + +The display_otr_message() operation was removed for the same reasons as +above for the notify() operation. + + +/* Return a newly allocated string containing a human-friendly name + * for the given protocol id */ +const char *(*protocol_name)(void *opdata, const char *protocol); + +/* Deallocate a string allocated by protocol_name */ +void (*protocol_name_free)(void *opdata, const char *protocol_name); + +The above operations are no longer required, as they were used when +preparing messages shown to users. + + +/* Log a message. The passed message will end in "\n". */ +void (*log_message)(void *opdata, const char *message); + +The log_message() operation was also replaced by message event +callbacks. + +3.1.2. Added Operations + +/* We received a request from the buddy to use the current "extra" + * symmetric key. The key will be passed in symkey, of length + * OTRL_EXTRAKEY_BYTES. The requested use, as well as use-specific + * data will be passed so that the applications can communicate other + * information (some id for the data transfer, for example). */ +void (*received_symkey)(void *opdata, ConnContext *context, + unsigned int use, const unsigned char *usedata, + size_t usedatalen, const unsigned char *symkey); + +This is called when a remote buddy has specified a use for the current +symmetric key. If your application does not use the extra symmetric key +it does not need to provide an implementation for this operation. + + +/* Return a string according to the error event. This string will then + * be concatenated to an OTR header to produce an OTR protocol error + * message. The following are the possible error events: + * - OTRL_ERRCODE_ENCRYPTION_ERROR + * occured while encrypting a message + * - OTRL_ERRCODE_MSG_NOT_IN_PRIVATE + * sent encrypted message to somebody who is not in + * a mutual OTR session + * - OTRL_ERRCODE_MSG_UNREADABLE + * sent an unreadable encrypted message + * - OTRL_ERRCODE_MSG_MALFORMED + * message sent is malformed */ +const char *(*otr_error_message)(void *opdata, ConnContext *context, + OtrlErrorCode err_code); + +/* Deallocate a string returned by otr_error_message */ +void (*otr_error_message_free)(void *opdata, const char *err_msg); + +These methods are for producing human-readable error message that will +be sent to the remote buddy when one of these error conditions occurs. +They will be appended to the string "?OTR Error: ". Implementing this +operation is not required, but depending on your application it may be a +good idea. + + +/* Return a string that will be prefixed to any resent message. If this + * function is not provided by the application then the default prefix, + * "[resent]", will be used. + * */ +const char *(*resent_msg_prefix)(void *opdata, ConnContext *context); + +/* Deallocate a string returned by resent_msg_prefix */ +void (*resent_msg_prefix_free)(void *opdata, const char *prefix); + +These operations give the option of chosing an alternative to the +English string "[resent]", when a message is resent. + + +/* Update the authentication UI with respect to SMP events + * These are the possible events: + * - OTRL_SMPEVENT_ASK_FOR_SECRET + * prompt the user to enter a shared secret. The sender application + * should call otrl_message_initiate_smp, passing NULL as the question. + * When the receiver application resumes the SM protocol by calling + * otrl_message_respond_smp with the secret answer. + * - OTRL_SMPEVENT_ASK_FOR_ANSWER + * (same as OTRL_SMPEVENT_ASK_FOR_SECRET but sender calls + * otrl_message_initiate_smp_q instead) + * - OTRL_SMPEVENT_CHEATED + * abort the current auth and update the auth progress dialog + * with progress_percent. otrl_message_abort_smp should be called to + * stop the SM protocol. + * - OTRL_SMPEVENT_INPROGRESS and + * OTRL_SMPEVENT_SUCCESS and + * OTRL_SMPEVENT_FAILURE and + * OTRL_SMPEVENT_ABORT + * update the auth progress dialog with progress_percent + * - OTRL_SMPEVENT_ERROR + * (same as OTRL_SMPEVENT_CHEATED) + * */ +void (*handle_smp_event)(void *opdata, OtrlSMPEvent smp_event, + ConnContext *context, unsigned short progress_percent, + char *question); + +These SMP events are initiated by otrl_message_receiving() when it has +received an SMP TLV from a remote buddy. If you application is +implementing support for SMP authentication it should handle these +events appropriately. + +Previously applications had to manually check, upon receiving messages, +whether the message contained any SMP TLVs that are relevant to the +current SMP state. + +/* Handle and send the appropriate message(s) to the sender/recipient + * depending on the message events. All the events only require an opdata, + * the event, and the context. The message and err will be NULL except for + * some events (see below). The possible events are: + * - OTRL_MSGEVENT_ENCRYPTION_REQUIRED + * Our policy requires encryption but we are trying to send + * an unencrypted message out. + * - OTRL_MSGEVENT_ENCRYPTION_ERROR + * An error occured while encrypting a message and the message + * was not sent. + * - OTRL_MSGEVENT_CONNECTION_ENDED + * Message has not been sent because our buddy has ended the + * private conversation. We should either close the connection, + * or refresh it. + * - OTRL_MSGEVENT_SETUP_ERROR + * A private conversation could not be set up. A gcry_error_t + * will be passed. + * - OTRL_MSGEVENT_MSG_REFLECTED + * Received our own OTR messages. + * - OTRL_MSGEVENT_MSG_RESENT + * The previous message was resent. + * - OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE + * Received an encrypted message but cannot read + * it because no private connection is established yet. + * - OTRL_MSGEVENT_RCVDMSG_UNREADABLE + * Cannot read the received message. + * - OTRL_MSGEVENT_RCVDMSG_MALFORMED + * The message received contains malformed data. + * - OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD + * Received a heartbeat. + * - OTRL_MSGEVENT_LOG_HEARTBEAT_SENT + * Sent a heartbeat. + * - OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR + * Received a general OTR error. The argument 'message' will + * also be passed and it will contain the OTR error message. + * - OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED + * Received an unencrypted message. The argument 'smessage' will + * also be passed and it will contain the plaintext message. + * - OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED + * Cannot recognize the type of OTR message received. + * - OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE + * Received and discarded a message intended for another instance. */ +void (*handle_msg_event)(void *opdata, OtrlMessageEvent msg_event, + ConnContext *context, const char *message, + gcry_error_t err); + +This operation is called when some type of exceptional event has occured +that your application may want to be aware of. Your application may want +to write an event to a log file, display a message to the user, or +ignore the event. While it is not required to implement this operation, +it is probably a good idea. + + +/* Create a instance tag for the given accountname/protocol if + * desired. */ +void (*create_instag)(void *opdata, const char *accountname, + const char *protocol); + +This is called when the library notices this account name and protocol +pair does not have an instance tag. Similar to create_privkey(), your +application may simply open a file handle and call: +gcry_error_t otrl_instag_generate_FILEp(OtrlUserState us, FILE *instf, + const char *accountname, const char *protocol) + +If you don't provide an implementation for this operation, a new +non-persistent instance tag will be randomly generated. One benefit to +having a persisted instance tag is that if your application closes and +re-opens during a private conversation, further messages you receive +from this buddy will correctly raise the +OTRL_MSGEVENT_RCVDMSG_UNREADABLE event instead of raising +OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE because destination instance +tag is now different from your own. + + +/* Called immediately before a data message is encrypted, and after a data + * message is decrypted. The OtrlConvertType parameter has the value + * OTRL_CONVERT_SENDING or OTRL_CONVERT_RECEIVING to differentiate these + * cases. */ +void (*convert_msg)(void *opdata, ConnContext *context, + OtrlConvertType convert_type, char ** dest, const char *src); + +/* Deallocate a string returned by convert_msg. */ +void (*convert_free)(void *opdata, ConnContext *context, char *dest); + +The convert_msg() operation is called immediately before a message is +encrypted and immediately after a message is decrypted. This callback +can tweak the plaintext message as needed. One use case would be for an +application to tweak formatting on the plaintext if, for example, this +is something that would normally be done on the plaintext by some other +entity while the message is in transit. + +/* When timer_control is called, turn off any existing periodic + * timer. + * + * Additionally, if interval > 0, set a new periodic timer + * to go off every interval seconds. When that timer fires, you + * must call otrl_message_poll(userstate, uiops, uiopdata); from the + * main libotr thread. + * + * The timing does not have to be exact; this timer is used to + * provide forward secrecy by cleaning up stale private state that + * may otherwise stick around in memory. Note that the + * timer_control callback may be invoked from otrl_message_poll + * itself, possibly to indicate that interval == 0 (that is, that + * there's no more periodic work to be done at this time). + * + * If you set this callback to NULL, then you must ensure that your + * application calls otrl_message_poll(userstate, uiops, uiopdata); + * from the main libotr thread every definterval seconds (where + * definterval can be obtained by calling + * definterval = otrl_message_poll_get_default_interval(userstate); + * right after creating the userstate). The advantage of + * implementing the timer_control callback is that the timer can be + * turned on by libotr only when it's needed. + * + * It is not a problem (except for a minor performance hit) to call + * otrl_message_poll more often than requested, whether + * timer_control is implemented or not. + * + * If you fail to implement the timer_control callback, and also + * fail to periodically call otrl_message_poll, then you open your + * users to a possible forward secrecy violation: an attacker that + * compromises the user's computer may be able to decrypt a handful + * of long-past messages (the first messages of an OTR + * conversation). + */ +void (*timer_control)(void *opdata, unsigned int interval); + +In order to prevent a forward secrecy violation, applications using +libotr now need to be able to call otrl_message_poll on occasion. The +simplest thing to do is just to set up a local timer that calls that +function every definterval = +otrl_message_poll_get_default_interval(userstate) seconds. To avoid +unnecessary overhead, however, the timer_control callback is available. +If you set timer_control to non-NULL, it will be called with +instructions to turn on or off the periodic timer, and to what interval. + +You must also be sure to turn off the timer before freeing your +userstate with otrl_userstate_free. + +3.2. Instance Tags + +If your application allows the same user to be logged in multiple times +from different locations, it should probably be aware of instance tags. +A user can maintain multiple concurrent OTR conversations with a buddy +who is logged in multiple times. Only one of the buddy's sessions can be +a client who is running OTR protocol version 2. When a user has a +conversation with a buddy who is running OTR protocol version 2, the +conversation is associated with a ConnContext that lists +"their_instance" as OTRL_INSTAG_MASTER (which has a value of 0). Each +version 3 conversation with the same buddy will have its own +ConnContext, which you can differentiate by the value in the +"their_instance" field. + +In the linked list of ConnContexts, the master context for a buddy is +always listed immediately before its children. Fingerprints are only +stored with the master context. Given a ConnContext associated to a +conversation with a buddy, you can easily iterate over all the contexts +for that buddy by doing the following: + +void example_something_happened(ConnContext * context) { + ConnContext * context_iter = context->m_context; + + while (context_iter && context_iter->m_context == context->m_context) { + /* Something you wish to affect all contexts of a particular buddy */ + context_iter = context_iter->next; + } + +If a user has multiple OTR sessions with the same buddy, your +application will likely want to provide some way for the user to select +which instance to send outgoing messages to. You can detect when a user +has multiple OTR sessions with the same buddy by iterating over the +ConnContexts of a buddy when a conversation has gone secure and checking +whether more than one is not in plaintext state. You specify which +instance outgoing messages are directed to in otrl_message_sending: + +gcry_error_t otrl_message_sending(OtrlUserState us, + const OtrlMessageAppOps *ops, + void *opdata, const char *accountname, const char *protocol, + const char *recipient, otrl_instag_t instag, const char *original_msg, + OtrlTLV *tlvs, char **messagep, OtrlFragmentPolicy fragPolicy, + ConnContext **contextp, + void (*add_appdata)(void *data, ConnContext *context), + void *data); + +Instead of an actual instance tag, you can specify a meta instance tag +(e.g., if the user has not made an explicit selection). Here are the +list of meta instance tags, as defined in instag.h: + +#define OTRL_INSTAG_BEST 1 /* Most secure, based on: conv status, + * then fingerprint status, then most recent. */ +#define OTRL_INSTAG_RECENT 2 /* Most recent of the two meta instances below */ +#define OTRL_INSTAG_RECENT_RECEIVED 3 +#define OTRL_INSTAG_RECENT_SENT 4 + +OTRL_INSTAG_BEST choses the instance that has the best conv status, then +fingerprint status (in the event of a tie), then most recent (similarly +in the event of a tie). When calculating how recent an instance has been +active, OTRL_INSTAG_BEST is limited by a one second resolution. +OTRL_INSTAG_RECENT* does not have this limitation, but due to inherent +uncertainty in some networks, libotr's notion of the most recent may not +always agree with the remote network. It is important to understand +this limitation due to the issue noted in the next paragraph. + +Note that instances do add some uncertainty when dealing with networks +that only deliver messages to the most recently active session for a +buddy who is logged in multiple times. If you have a particular instance +selected, and the IM network is simply not going to deliver to that +particular instance, there isn't too much libotr can do. In this case, +you may want your application to warn when a user has selected an +instance that is not the most recent. + +To explicitly specify the destination instance of a protocol version 2 +conversation with a particular buddy, the instag value is +OTRL_INSTAG_MASTER. + +To look up a ConnContext associated with a particular instance (or meta- +instance), specify the instance in otrl_context_find(): + +ConnContext * otrl_context_find(OtrlUserState us, const char *user, + const char *accountname, const char *protocol, + otrl_instag_t their_instance, int add_if_missing, int *addedp, + void (*add_app_data)(void *data, ConnContext *context), void *data) + +If your application persists instance tags, when it starts up, it should +call one the following functions to read the persisted instance tags: + +gcry_error_t otrl_instag_read(OtrlUserState us, const char *filename); +gcry_error_t otrl_instag_read_FILEp(OtrlUserState us, FILE *instf); + +It would make sense to do this immediately after your application has +read stored privkeys and fingerprints. + +3.3. Fragmentation Changes + +In libotr version 3.2.0, you would need to call otrl_message_sending() +to create an encrypted message, and then call fragment_and_send() to +get libotr to fragment and inject that message. In libotr 4.0.0, the +functionality of fragment_and_send() has been integrated into +otrl_message_sending(). Simply specify an OtrlFragmentPolicy to +otrl_message_sending(). The fragmentation policies are the same as +before, and an addition policy "OTRL_FRAGMENT_SEND_SKIP" has been added +for cases when fragmentation is not desired. + +3.4. Asynchronous Private Key Generation + +An application that wants to begin asynchronous key generation calls the +following method: + +/* Begin a private key generation that will potentially take place in + * a background thread. This routine must be called from the main + * thread. It will set *newkeyp, which you can pass to + * otrl_privkey_generate_calculate in a background thread. If it + * returns gcry_error(GPG_ERR_EEXIST), then a privkey creation for + * this accountname/protocol is already in progress, and *newkeyp will + * be set to NULL. */ +gcry_error_t otrl_privkey_generate_start(OtrlUserState us, + const char *accountname, const char *protocol, void **newkeyp) + +A background thread can call the following method with the structure +that was passed into "newkeyp" above: + +/* Do the private key generation calculation. You may call this from a + * background thread. When it completes, call + * otrl_privkey_generate_finish from the _main_ thread. */ +gcry_error_t otrl_privkey_generate_calculate(void *newkey) + + +Upon completion the application would call: + +/* Call this from the main thread only. It will write the newly created + * private key into the given file and store it in the OtrlUserState. */ +gcry_error_t otrl_privkey_generate_finish(OtrlUserState us, + void *newkey, const char *filename) + +If the privkey generation was cancelled, the application should call: + +/* Call this from the main thread only, in the event that the background + * thread generating the key is cancelled. The newkey is deallocated, + * and must not be used further. */ +void otrl_privkey_generate_cancelled(OtrlUserState us, void *newkey) + + +3.5. Library Initialization + +If you currently initialize libotr with the recommended OTRL_INIT; +macro, you do not need to change anything. + +If you call otrl_init(ver_major, ver_minor, ver_sub) directly, then know +that this function no longer returns void. Previously, if the +application requested version numbers incompatible with those of the +library, the library would exit(1). Now, the otrl_init call will return +a non-zero error code. You must check the return value of otrl_init (a +gcry_error_t), and if it is non-zero, your application's expected +API/ABI does not match the installed libotr, and libotr cannot be used. + |