├── .gitignore ├── AUTHORS ├── COPYING ├── COPYING.LIB ├── ChangeLog ├── INSTALL ├── Makefile.am ├── NEWS ├── Protocol-v1.txt ├── Protocol-v2.html ├── Protocol-v3.html ├── README ├── UPGRADING ├── bootstrap ├── configure.ac ├── libotr.m4 ├── libotr.pc.in ├── makedist ├── packaging └── fedora │ └── libotr.spec ├── src ├── Makefile.am ├── auth.c ├── auth.h ├── b64.c ├── b64.h ├── context.c ├── context.h ├── context_priv.c ├── context_priv.h ├── dh.c ├── dh.h ├── instag.c ├── instag.h ├── mem.c ├── mem.h ├── message.c ├── message.h ├── privkey-t.h ├── privkey.c ├── privkey.h ├── proto.c ├── proto.h ├── serial.h ├── sm.c ├── sm.h ├── tests.c ├── tlv.c ├── tlv.h ├── userstate.c ├── userstate.h └── version.h ├── test_suite ├── README ├── dummy_im.py ├── dummy_im │ └── dummy_client.py ├── instance_tags0.txt ├── instance_tags1.txt ├── instance_tags2.txt ├── instance_tags3.txt ├── instance_tags4.txt ├── otr.private_key ├── otr_c_client │ ├── Makefile │ ├── README │ └── dummy_client.c ├── otr_subprocess.py ├── otr_test.py ├── otr_test_general.py └── otr_test_mixed.py └── toolkit ├── Makefile.am ├── aes.c ├── aes.h ├── ctrmode.c ├── ctrmode.h ├── otr_mackey.c ├── otr_modify.c ├── otr_parse.c ├── otr_readforge.c ├── otr_remac.c ├── otr_sesskeys.c ├── otr_toolkit.1 ├── parse.c ├── parse.h ├── readotr.c ├── readotr.h ├── sesskeys.c ├── sesskeys.h ├── sha1hmac.c └── sha1hmac.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.swp 4 | *.o 5 | *.swo 6 | *.pyc 7 | Makefile 8 | .libs/ 9 | .deps/ 10 | *~ 11 | *.la 12 | *.lo 13 | Makefile.in 14 | *.loT 15 | *.info 16 | *.gz 17 | *.tar 18 | .dirstamp 19 | configure 20 | aclocal.m4 21 | autom4te.cache/ 22 | config.h 23 | config.h.in 24 | config.log 25 | config.status 26 | stamp-h1 27 | libtool 28 | tags 29 | 30 | # m4 macros not automatically generated 31 | /config/ 32 | 33 | libotr.pc 34 | 35 | /toolkit/otr_mackey 36 | /toolkit/otr_modify 37 | /toolkit/otr_parse 38 | /toolkit/otr_readforge 39 | /toolkit/otr_remac 40 | /toolkit/otr_sesskeys 41 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Off-the-Record Messaging Library and Toolkit 2 | 3 | Authors: 4 | 5 | Ian Goldberg, David Goulet, Rob Smits, Chris Alexander, Willy Lew, 6 | Lisa Du, Nikita Borisov 7 | 8 | 9 | See the README file for mailing list information 10 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | REQUIREMENTS 2 | 3 | To compile the OTR library and toolkit, you'll need at least: 4 | - libgpg-error 1.0 [ftp://ftp.gnupg.org/gcrypt/libgpg-error/] 5 | - libgcrypt 1.2.0 [ftp://ftp.gnupg.org/gcrypt/libgcrypt/] 6 | 7 | If you install these with a package manager, you'll probably need the 8 | -dev or -devel versions of the packages. 9 | 10 | On Fedora, these packages are: 11 | libgpg-error-devel libgcrypt-devel 12 | 13 | On Debian (testing or unstable), they are: 14 | libgpg-error-dev libgcrypt11-dev 15 | 16 | COMPILING 17 | 18 | If you're got a CVS copy, you will need to regenerate the configure 19 | script using: 20 | 21 | ./bootstrap 22 | 23 | Once you have the configure script (which comes with the source 24 | deistribution), run it with the "--with-pic" option, as well as any 25 | other options that may be necessary for your system. Some examples: 26 | 27 | Linux: 28 | ./configure --with-pic --prefix=/usr --mandir=/usr/share/man 29 | 30 | NETBSD: 31 | CPPFLAGS="-I/usr/pkg/include" LDFLAGS="-R/usr/pkg/lib -L/usr/pkg/lib" \ 32 | ./configure --with-pic --prefix=/usr/pkg 33 | 34 | mingw cross-compiler from Debian Linux: 35 | ./configure --with-pic --host=i586-mingw32msvc \ 36 | --prefix=/usr/i586-mingw32msvc 37 | 38 | Once the configure script writes a Makefile, you should be able to just 39 | run "make". 40 | 41 | INSTALLATION 42 | 43 | You should be able to simply do "make install". If you want to install 44 | somewhere other than / (this is useful for package creators), use 45 | something like "make DESTDIR=/path/to/install/to install". 46 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS = -I config 2 | 3 | SUBDIRS = src toolkit 4 | 5 | EXTRA_DIST = Protocol-v3.html UPGRADING packaging libotr.m4 libotr.pc.in 6 | 7 | aclocaldir = $(datadir)/aclocal 8 | aclocal_DATA = libotr.m4 9 | 10 | pkgconfigdir = $(libdir)/pkgconfig 11 | pkgconfig_DATA = libotr.pc 12 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | 21 Oct 2014: 2 | - Release 4.1.0 3 | - Modernized autoconf build system 4 | - Use constant-time comparisons where needed 5 | - Use gcrypt secure memory allocation 6 | - Correctly reject attempts to fragment a message into too many pieces 7 | - Fix a missing opdata when sending message fragments 8 | - Don't lose the first user message when REQUIRE_ENCRYPTION is set 9 | - Fix some memory leaks 10 | - Correctly check for children contexts' state when forgetting a context 11 | - API Changes: 12 | - Added API functions otrl_context_find_recent_instance and 13 | otrl_context_find_recent_secure_instance. 14 | 15 | 24 Aug 2012: 16 | - Release 4.0.0 17 | - Support v3 of the OTR protocol 18 | - The main new feature: sensibly handle the case where a user is logged 19 | in multiple times to the same IM account 20 | - API changes: 21 | - instance tags, to support multiple simultaneous logins 22 | - support for asynchronous private key generation 23 | - the ability to provide an "extra" symmetric key to applications 24 | (with forward secrecy) 25 | - applications can supply a formation conversion callback if they do 26 | not natively use XHTML-style UTF8 markup 27 | - error messages formerly provided by libotr are now handled using 28 | callbacks to the application, for better i18n support 29 | - otrl_message_sending now handles message fragmentation internally 30 | 31 | 27 May 2008: 32 | - Added support for one-way authentication using an explicit question, 33 | based on the SOUPS 2008 user study. 34 | 35 | 1 Aug 2007: 36 | - Released 3.1.0 37 | 38 | 24 Jul 2007: 39 | - Added fragmentation support for large messages 40 | - Added new method for buddy authentication which does not require the 41 | (explicit) use of fingerprints. 42 | 43 | 02 Nov 2005: 44 | - Released 3.0.0 45 | 46 | 16 Oct 2005: 47 | - Major overhaul with implementation of version 2 of the protocol. 48 | 49 | 24 Jun 2005: 50 | - Remove the "confirm_fingerprint" callback which requires the user to 51 | acknowledge the new fingerprint before it can be used. Replace it 52 | with a "new_fingerprint" callback which merely informs the user that a 53 | new fingerprint has been received. 54 | - Allow the app to set a "trust level" for fingerprints. This is an 55 | arbitrary string, intended to indicate whether (or possibly by what 56 | means) the user has verified that this fingerprint is accurate. 57 | - Clarify that, if the user requests to see the secure session id in 58 | the middle of the conversation, the value displayed should be the one 59 | calculated at the time the private connection was established (the 60 | last Key Exchange Message that caused a rekeying), _not_ the DH secure 61 | id calculated from DH keys in more recent Data Messages. 62 | 63 | 03 May 2005: 64 | - Released 2.0.2 65 | 66 | 16 Feb 2005: 67 | - Released 2.0.1 68 | - Don't send encrypted messages to a buddy who has disconnected his 69 | private connection with us. 70 | - Don't show the user the "the last message was resent" notice if the 71 | message has never actually been sent before. 72 | - Fix a crash bug that happened when messages were retransmitted under 73 | certain circumstances. 74 | 75 | 08 Feb 2005: 76 | - Released 2.0.0 77 | - Keep track of whether a given message is eligible for retransmission 78 | 79 | 02 Feb 2005: 80 | - Released 1.99.0, the first preview release of 2.0.0 81 | 82 | 31 Jan 2005: 83 | - Machine-readable records can now be attached to Data Messages inside 84 | the private channel. 85 | 86 | 30 Jan 2005: 87 | - New OtrlUserState datatype encapsulates private keys and known 88 | fingerprints, instead of having a single global list. 89 | - Added libotr.m4 for helping to autoconfiscate packages that use 90 | libotr. 91 | - Resend the last message if it caused a re-keying. 92 | - New OtrlPolicy datatype allows you to specify a per-connection OTR 93 | policy: never use OTR, OTR only if manually requested, automatically 94 | start OTR if possible, refuse to *not* use OTR. 95 | - New callbacks: display_otr_message, policy, is_logged_in 96 | 97 | 22 Jan 2005: 98 | - Released 1.0.4 99 | - Log, but otherwise ignore, unrecognized OTR messages. 100 | - Initial autoconfiscation, thanks to Greg Troxel . 101 | 102 | 18 Jan 2005: 103 | - Released 1.0.3 104 | - Split gaim-otr and libotr into separate packages. 105 | 106 | 13 Jan 2005: 107 | - Generate private keys automatically, if needed. Show a Please Wait 108 | dialog while this is happening. 109 | - We may as well try to use the "tag" method of checking for OTR, even 110 | when we don't already know a fingerprint for the correspondent. 111 | - Add version checking to the otrl_init() call. 112 | 113 | 12 Jan 2005: 114 | - Refactored the logic parts of gaim-otr into libotr, so they can be 115 | shared by other libotr-enabled apps. 116 | 117 | 21 Dec 2004: 118 | - Released 1.0.2 119 | - If a Man-in-the-Middle steals both Alice's and Bob's DSA private keys, 120 | he can perform a birthday attack to try to get his session id with 121 | each end to match. Since the session id was only 64 bits long, his 122 | work was only 2^32, which is not enough. We now make the session id 123 | the whole SHA-1 hash, instead of truncating it. 124 | - Made otr_sesskeys output the calculated public key as well, for added 125 | ease of forging messages when you don't know any plaintext. 126 | 127 | 14 Dec 2004: 128 | - Released 1.0.1 129 | - Added a more sensible error message in the event that we receive our 130 | own OTR Key Exchange messages. 131 | - If we're about to send a plaintext message to a correspondent for whom 132 | we've got a fingerprint, append a special (whitespace) OTR tag 133 | sequence. The other side (if in fact running OTR) will recognize it 134 | and start a Key Exchange. 135 | 136 | 12 Dec 2004: 137 | - Released 1.0.0 138 | 139 | 11 Dec 2004: 140 | - OTR button now gets sensitized and desensitized along with the other 141 | buttons in the conversation window when you log in and out of 142 | accounts. 143 | 144 | 10 Dec 2004: 145 | - Released 0.9.9rc2 146 | - Heartbeats now only get sent if (1) we have just received a message, 147 | and (2) we haven't sent one to that user in over a minute. 148 | 149 | 09 Dec 2004: 150 | - Back out of the sending of heartbeats. They were causing too many 151 | problems. It seems some networks don't let buddies know when you 152 | log out, and then you get a dialog box "unable to send message" each 153 | minute. :-( 154 | 155 | 08 Dec 2004: 156 | - Released 0.9.9rc1 157 | - Removed the 100 private connection limit, by not using a fixed amount 158 | of secure memory. Unfortuantely, this means that *no* memory is 159 | pinned any more, but pinning only ever happened before in the unlikely 160 | event you ran gaim as root. 161 | - Changed the "Private connection with (username) refreshed" dialog at 162 | Paul's request so that it's no longer in "scary" "evil" bold, and 163 | rephrased it so it's less likely to be misread as "refused" instead of 164 | "refreshed". ;-) 165 | - We now send heartbeats (OTR Data Messages with an empty message part) 166 | once a minute, to anyone we're confident is still online. If both 167 | sides are doing this, then keys get rotated regularly, even if one 168 | or both sides aren't actively typing. This aids perfect forward 169 | secrecy. 170 | 171 | 04 Dec 2004: 172 | - Fixed a bug wherein multi-person chat windows would get the OTR button 173 | in their button bar if the OTR plugin was enabled when one of them was 174 | active. 175 | 176 | 03 Dec 2004: 177 | - Released 0.9.1 178 | 179 | 02 Dec 2004: 180 | - Clicking "OTR: Private" when you're already private will display an 181 | info dialog letting you know the connection was refreshed (assuming it 182 | actually is; if the other side isn't running OTR at all, the dialog 183 | doesn't show, and if the other side had lost its private connection, a 184 | new one will be established, with the "new private connection" dialog 185 | displayed to each side (as before)). 186 | - The toolip for "OTR: Private" is now "Refresh the private connection". 187 | - "make install" now depends on "make all". 188 | - Added man page for OTR toolkit programs 189 | - Log a debug message when we receive and discard a heartbeat 190 | 191 | 1 Dec 2004: 192 | - Fixed the Makefiles so that "make clean" also removes the binaries 193 | - Fixed the Makefiles so that they install into DESTDIR 194 | - Added packaging/debian 195 | 196 | 30 Nov 2004: 197 | - Released 0.9.0 198 | - Included the OTR Messaging Toolkit. See the README for details. 199 | 200 | 28 Nov 2004: 201 | - Finished the Protocol document 202 | - Changed the name of the plugin binary from "otr-plugin.so" to 203 | "gaim-otr.so". *** NOTE: this means you'll have to (1) remove the 204 | old otr-plugin.so file from your plugins directory, and (2) re-enable 205 | the Off-the-Record Messaging plugin in the Preferences panel. 206 | - Included MAC keys used to create messages in the revealed MAC section 207 | of the Data message, in addition to MAC keys used to verify messages. 208 | - Set all exported symbols to start with otrl_ (for the library) or 209 | otrg_ (for the gaim plugin), in preparation for moving the pieces 210 | into their own directories. 211 | - If we receive a Data message with no actual message in it, don't 212 | display it to the user. This may eventually be useful for doing 213 | "heartbeat" key rotations. 214 | - Separated libotr and gaim-otr into their own directories. 215 | 216 | 27 Nov 2004: 217 | - Switched from using gaim_notify_* to a slightly modified version that 218 | doesn't grab the focus 219 | 220 | 26 Nov 2004: 221 | - Put all the cipher operations in secure memory. This makes each 222 | private connection take 9472 bytes of secure memory, so we up the 223 | available amount of secure memory to 100 times that. Eventually, 224 | we'd like to make this dynamically grow. 225 | 226 | 25 Nov 2004: 227 | - Released 0.8.3 228 | - Don't put the DSA keys in libgcrypt secure memory, since (a) we read 229 | them off disk anyway, and (b) we want to avoid running out of secure 230 | memory. 231 | - Removed the "Do you want to start a private conversation" dialogs when 232 | one side in encrypted and the other side isn't, and instead just try 233 | to start one if we know for sure the other side supports it. 234 | - Sped up the DH computations by using a 320-bit exponent. 235 | 236 | 23 Nov 2004: 237 | - Released 0.8.2 238 | - There was a crash if you received an OTR Query before setting up a 239 | private key. Fixed. 240 | - The fingerprint in the UI is now selectable, for cut/paste. 241 | - *** Protocol change. We're no longer backward compatible. 242 | - The "revealed MAC keys" moved out of the MAC'd region of the data 243 | packet. It's not wrong where it is, but it's more obviously 244 | correct in the new place. 245 | 246 | 22 Nov 2004: 247 | - Released 0.8.1 248 | - Jabber wasn't working, for two reasons: 249 | - it sticks ... around the message 250 | - it refers to the same user by multiple names; e.g. "user@jabber.org" 251 | vs. "user@jabber.org/Gaim" 252 | Both are now fixed: we look for the OTR message anywhere in the packet 253 | now, not just at the beginning, and we normalize all usernames. 254 | - Each account now has its own private key / fingerprint 255 | - This is so you don't automatically leak the information that the 256 | accounts are owned by the same person 257 | - There's a better indicator of private / not private status in the 258 | conversation window, which you can click to start the private 259 | communication. 260 | 261 | 21 Nov 2004: 262 | - Initial 0.8.0 release 263 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | if [ ! -e config ]; then 6 | mkdir config 7 | fi 8 | 9 | autoreconf -i 10 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl Process this file with autoconf to produce configure. 2 | 3 | dnl Notes on version numbering: 4 | dnl For an implementation-only change: 5 | dnl Change the libotr package version from a.b.c to a.b.(c+1) 6 | dnl Change the libotr libtool version from x:y:z to x:(y+1):z 7 | dnl For a backwards-compatible API change (e.g. adding functions): 8 | dnl Change the libotr package version from a.b.c to a.(b+1).0 9 | dnl Change the libotr libtool version from x:y:z to (x+1):0:(z+1) 10 | dnl [Note that this does *not* change the major number of the .so.] 11 | dnl For a backwards-incompatible API change (e.g. changing data structures): 12 | dnl Change the libotr package version from a.b.c to (a+1).0.0 13 | dnl Change the libotr libtool version from x:y:z to (x+1):0:0 14 | AC_INIT([libotr],[4.1.0],[otr@cypherpunks.ca],[],[https://otr.cypherpunks.ca]) 15 | 16 | AM_CONFIG_HEADER(config.h) 17 | AC_CONFIG_AUX_DIR([config]) 18 | 19 | AM_INIT_AUTOMAKE 20 | LIBOTR_LIBTOOL_VERSION="6:0:1" 21 | 22 | AC_CONFIG_MACRO_DIR([config]) 23 | # Silent compilation so warnings can be spotted. 24 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) 25 | 26 | AC_SUBST(LIBOTR_LIBTOOL_VERSION) 27 | 28 | AC_PROG_CC 29 | LT_INIT 30 | 31 | AM_PATH_LIBGCRYPT(1:1.2.0,,AC_MSG_ERROR(libgcrypt 1.2.0 or newer is required.)) 32 | 33 | dnl 1:flags 34 | dnl Taken from Tor's autoconf magic repository 35 | AC_DEFUN([OTR_CHECK_CFLAGS], [ 36 | AS_VAR_PUSHDEF([VAR],[otr_cv_cflags_$1]) 37 | AC_CACHE_CHECK([whether the compiler accepts $1], VAR, [ 38 | otr_saved_CFLAGS="$CFLAGS" 39 | CFLAGS="$CFLAGS -pedantic -Werror $1" 40 | AC_TRY_COMPILE([], [return 0;], 41 | [AS_VAR_SET(VAR,yes)], 42 | [AS_VAR_SET(VAR,no)]) 43 | CFLAGS="$otr_saved_CFLAGS" 44 | ]) 45 | if test x$VAR = xyes; then 46 | CFLAGS="$CFLAGS $1" 47 | fi 48 | AS_VAR_POPDEF([VAR]) 49 | ]) 50 | 51 | dnl 1:flags 52 | dnl 2:extra ldflags 53 | dnl 3:extra libraries 54 | AC_DEFUN([OTR_CHECK_LDFLAGS], [ 55 | AS_VAR_PUSHDEF([VAR],[otr_cv_ldflags_$1]) 56 | AC_CACHE_CHECK([whether the linker accepts $1], VAR, [ 57 | otr_saved_CFLAGS="$CFLAGS" 58 | otr_saved_LDFLAGS="$LDFLAGS" 59 | otr_saved_LIBS="$LIBS" 60 | CFLAGS="$CFLAGS -pedantic -Werror" 61 | LDFLAGS="$LDFLAGS $2 $1" 62 | LIBS="$LIBS $3" 63 | AC_RUN_IFELSE([AC_LANG_PROGRAM([#include ], [fputs("", stdout)])], 64 | [AS_VAR_SET(VAR,yes)], 65 | [AS_VAR_SET(VAR,no)], 66 | [AC_TRY_LINK([], [return 0;], 67 | [AS_VAR_SET(VAR,yes)], 68 | [AS_VAR_SET(VAR,no)])]) 69 | CFLAGS="$otr_saved_CFLAGS" 70 | LDFLAGS="$otr_saved_LDFLAGS" 71 | LIBS="$otr_saved_LIBS" 72 | ]) 73 | if test x$VAR = xyes; then 74 | LDFLAGS="$LDFLAGS $1" 75 | fi 76 | AS_VAR_POPDEF([VAR]) 77 | ]) 78 | 79 | 80 | dnl If _WIN32 is defined and non-zero, we are building for win32 81 | AC_MSG_CHECKING([for win32]) 82 | AC_RUN_IFELSE([AC_LANG_SOURCE([ 83 | int main(int c, char **v) { 84 | #ifdef _WIN32 85 | #if _WIN32 86 | return 0; 87 | #else 88 | return 1; 89 | #endif 90 | #else 91 | return 2; 92 | #endif 93 | }])], 94 | bwin32=true; AC_MSG_RESULT([yes]), 95 | bwin32=false; AC_MSG_RESULT([no]), 96 | bwin32=cross; AC_MSG_RESULT([cross]) 97 | ) 98 | 99 | if test "$bwin32" = cross; then 100 | AC_MSG_CHECKING([for win32 (cross)]) 101 | AC_COMPILE_IFELSE([AC_LANG_SOURCE([ 102 | #ifdef _WIN32 103 | int main(int c, char **v) {return 0;} 104 | #else 105 | #error 106 | int main(int c, char **v) {return x(y);} 107 | #endif 108 | ])], 109 | bwin32=true; AC_MSG_RESULT([yes]), 110 | bwin32=false; AC_MSG_RESULT([no])) 111 | fi 112 | 113 | AM_CONDITIONAL(BUILD_NT_SERVICES, test x$bwin32 = xtrue) 114 | 115 | dnl Adam Shostack suggests the following for Windows: 116 | dnl -D_FORTIFY_SOURCE=2 -fstack-protector-all 117 | dnl Others suggest '/gs /safeseh /nxcompat /dynamicbase' for non-gcc on Windows 118 | dnl This requires that we use gcc and that we add -O2 to the CFLAGS. 119 | AC_ARG_ENABLE(gcc-hardening, 120 | AS_HELP_STRING(--disable-gcc-hardening, disable compiler security checks)) 121 | 122 | dnl Linker hardening options 123 | dnl Currently these options are ELF specific - you can't use this with MacOSX 124 | AC_ARG_ENABLE(linker-hardening, 125 | AS_HELP_STRING(--disable-linker-hardening, disable linker security fixups)) 126 | 127 | dnl --------------------------------------------------------------------- 128 | dnl Now that we know about our major libraries, we can check for compiler 129 | dnl and linker hardening options. We need to do this with the libraries known, 130 | dnl since sometimes the linker will like an option but not be willing to 131 | dnl use it with a build of a library. 132 | 133 | all_ldflags_for_check="$LDFLAGS" 134 | all_libs_for_check="$LIBGCRYPT_LIBS" 135 | 136 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [ 137 | #if !defined(__clang__) 138 | #error 139 | #endif 140 | ])], have_clang=yes, have_clang=no) 141 | 142 | if test x$enable_gcc_hardening != xno; then 143 | CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2" 144 | if test x$have_clang = xyes; then 145 | OTR_CHECK_CFLAGS(-Qunused-arguments) 146 | fi 147 | OTR_CHECK_CFLAGS(-fstack-protector-all) 148 | OTR_CHECK_CFLAGS(-Wstack-protector) 149 | OTR_CHECK_CFLAGS(-fwrapv) 150 | 151 | dnl Ian added the next four: 152 | OTR_CHECK_CFLAGS(-fno-strict-overflow) 153 | OTR_CHECK_CFLAGS(-Wall) 154 | OTR_CHECK_CFLAGS(-Wextra -Wno-unused-parameter) 155 | OTR_CHECK_CFLAGS(-Wformat-security) 156 | 157 | OTR_CHECK_CFLAGS(--param ssp-buffer-size=1) 158 | if test "$bwin32" = "false"; then 159 | OTR_CHECK_CFLAGS(-fPIE) 160 | OTR_CHECK_LDFLAGS(-pie, "$all_ldflags_for_check", "$all_libs_for_check") 161 | else 162 | OTR_CHECK_CFLAGS(-fPIE) 163 | OTR_CHECK_LDFLAGS(-pie, "$all_ldflags_for_check", "$all_libs_for_check") 164 | OTR_CHECK_LDFLAGS([-Wl,--dynamicbase], "$all_ldflags_for_check", "$all_libs_for_check") 165 | OTR_CHECK_LDFLAGS([-Wl,--nxcompat], "$all_ldflags_for_check", "$all_libs_for_check") 166 | fi 167 | fi 168 | 169 | if test x$enable_linker_hardening != xno; then 170 | OTR_CHECK_LDFLAGS(-z relro -z now, "$all_ldflags_for_check", "$all_libs_for_check") 171 | fi 172 | 173 | AC_CONFIG_FILES([ 174 | Makefile 175 | src/Makefile 176 | toolkit/Makefile 177 | libotr.pc 178 | ]) 179 | 180 | AC_OUTPUT 181 | -------------------------------------------------------------------------------- /libotr.m4: -------------------------------------------------------------------------------- 1 | dnl 2 | dnl Off-the-Record Messaging library 3 | dnl Copyright (C) 2004-2007 Ian Goldberg, Chris Alexander, Nikita Borisov 4 | dnl 5 | dnl 6 | dnl This library is free software; you can redistribute it and/or 7 | dnl modify it under the terms of version 2.1 of the GNU Lesser General 8 | dnl Public License as published by the Free Software Foundation. 9 | dnl 10 | dnl This library is distributed in the hope that it will be useful, 11 | dnl but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | dnl Lesser General Public License for more details. 14 | dnl 15 | dnl You should have received a copy of the GNU Lesser General Public 16 | dnl License along with this library; if not, write to the Free Software 17 | dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | dnl 19 | 20 | dnl AM_PATH_LIBOTR([MINIMUM-VERSION [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) 21 | dnl Test for libotr, and define LIBOTR_CFLAGS and LIBOTR_LIBS as appropriate. 22 | dnl enables arguments --with-libotr-prefix= 23 | dnl --with-libotr-inc-prefix= 24 | dnl 25 | dnl You must already have found libgcrypt with AM_PATH_LIBGCRYPT 26 | dnl 27 | dnl Adapted from alsa.m4, originally by 28 | dnl Richard Boulton 29 | dnl Christopher Lansdown 30 | dnl Jaroslav Kysela 31 | 32 | AC_DEFUN([AM_PATH_LIBOTR], 33 | [dnl Save the original CFLAGS, LDFLAGS, and LIBS 34 | libotr_save_CFLAGS="$CFLAGS" 35 | libotr_save_LDFLAGS="$LDFLAGS" 36 | libotr_save_LIBS="$LIBS" 37 | libotr_found=yes 38 | 39 | dnl 40 | dnl Get the cflags and libraries for libotr 41 | dnl 42 | AC_ARG_WITH(libotr-prefix, 43 | [ --with-libotr-prefix=PFX Prefix where libotr is installed(optional)], 44 | [libotr_prefix="$withval"], [libotr_prefix=""]) 45 | 46 | AC_ARG_WITH(libotr-inc-prefix, 47 | [ --with-libotr-inc-prefix=PFX Prefix where libotr includes are (optional)], 48 | [libotr_inc_prefix="$withval"], [libotr_inc_prefix=""]) 49 | 50 | dnl Add any special include directories 51 | AC_MSG_CHECKING(for libotr CFLAGS) 52 | if test "$libotr_inc_prefix" != "" ; then 53 | LIBOTR_CFLAGS="$LIBOTR_CFLAGS -I$libotr_inc_prefix" 54 | CFLAGS="$CFLAGS $LIBOTR_CFLAGS" 55 | fi 56 | AC_MSG_RESULT($LIBOTR_CFLAGS) 57 | 58 | dnl add any special lib dirs 59 | AC_MSG_CHECKING(for libotr LIBS) 60 | if test "$libotr_prefix" != "" ; then 61 | LIBOTR_LIBS="$LIBOTR_LIBS -L$libotr_prefix" 62 | LDFLAGS="$LDFLAGS $LIBOTR_LIBS" 63 | fi 64 | 65 | dnl add the libotr library 66 | LIBOTR_LIBS="$LIBOTR_LIBS -lotr" 67 | LIBS="$LIBOTR_LIBS $LIBS" 68 | AC_MSG_RESULT($LIBOTR_LIBS) 69 | 70 | dnl Check for a working version of libotr that is of the right version. 71 | min_libotr_version=ifelse([$1], ,3.0.0,$1) 72 | no_libotr="" 73 | libotr_min_major_version=`echo $min_libotr_version | \ 74 | sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` 75 | libotr_min_minor_version=`echo $min_libotr_version | \ 76 | sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` 77 | libotr_min_sub_version=`echo $min_libotr_version | \ 78 | sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` 79 | AC_MSG_CHECKING(for libotr headers version $libotr_min_major_version.x >= $min_libotr_version) 80 | 81 | AC_LANG_SAVE 82 | AC_LANG_C 83 | AC_TRY_COMPILE([ 84 | #include 85 | #include 86 | ], [ 87 | # if(OTRL_VERSION_MAJOR != $libotr_min_major_version) 88 | # error not present 89 | # else 90 | 91 | # if(OTRL_VERSION_MINOR > $libotr_min_minor_version) 92 | exit(0); 93 | # else 94 | # if(OTRL_VERSION_MINOR < $libotr_min_minor_version) 95 | # error not present 96 | # endif 97 | 98 | # if(OTRL_VERSION_SUB < $libotr_min_sub_version) 99 | # error not present 100 | # endif 101 | # endif 102 | # endif 103 | exit(0); 104 | ], 105 | [AC_MSG_RESULT(found.)], 106 | [AC_MSG_RESULT(not present.) 107 | ifelse([$3], , [AC_MSG_ERROR(Sufficiently new version of libotr not found.)]) 108 | libotr_found=no] 109 | ) 110 | AC_LANG_RESTORE 111 | 112 | dnl Now that we know that we have the right version, let's see if we have the library and not just the headers. 113 | AC_CHECK_LIB([otr], [otrl_message_receiving],, 114 | [ifelse([$3], , [AC_MSG_ERROR(No linkable libotr was found.)]) 115 | libotr_found=no], 116 | $LIBGCRYPT_LIBS 117 | ) 118 | 119 | LDFLAGS="$libotr_save_LDFLAGS" 120 | LIBS="$libotr_save_LIBS" 121 | 122 | if test "x$libotr_found" = "xyes" ; then 123 | ifelse([$2], , :, [$2]) 124 | else 125 | LIBOTR_CFLAGS="" 126 | LIBOTR_LIBS="" 127 | ifelse([$3], , :, [$3]) 128 | fi 129 | 130 | dnl That should be it. Now just export our symbols: 131 | AC_SUBST(LIBOTR_CFLAGS) 132 | AC_SUBST(LIBOTR_LIBS) 133 | ]) 134 | 135 | -------------------------------------------------------------------------------- /libotr.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: libotr 7 | Description: Off-the-Record Messaging Library 8 | Version: @VERSION@ 9 | URL: https://otr.cypherpunks.ca/ 10 | Libs: -L${libdir} -lotr 11 | Cflags: -I${includedir} 12 | -------------------------------------------------------------------------------- /makedist: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Make the distribution tar.gz file from the CVS exported version 4 | 5 | autoreconf -s -i 6 | ./configure --mandir=/usr/share/man --prefix=/usr --with-pic 7 | fakeroot make dist 8 | -------------------------------------------------------------------------------- /packaging/fedora/libotr.spec: -------------------------------------------------------------------------------- 1 | %global snapshot 0 2 | Summary: Off-The-Record Messaging library and toolkit 3 | Name: libotr 4 | Version: 4.0.0 5 | Release: 1%{?dist} 6 | License: GPLv2 and LGPLv2 7 | Group: System Environment/Libraries 8 | Source0: https://otr.cypherpunks.ca/%{name}-%{version}.tar.gz 9 | Url: https://otr.cypherpunks.ca/ 10 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) 11 | Provides: libotr-toolkit = %{version} 12 | Obsoletes: libotr-toolkit < %{version} 13 | Requires: libgcrypt >= 1.2.0 14 | Requires: pkgconfig 15 | BuildRequires: libgcrypt-devel >= 1.2.0, libgpg-error-devel 16 | %if %{snapshot} 17 | Buildrequires: libtool automake autoconf 18 | %endif 19 | 20 | %description 21 | Off-the-Record Messaging Library and Toolkit 22 | This is a library and toolkit which implements Off-the-Record (OTR) Messaging. 23 | OTR allows you to have private conversations over IM by providing Encryption, 24 | Authentication, Deniability and Perfect forward secrecy. 25 | 26 | %package devel 27 | Summary: Development library and include files for libotr 28 | Group: Development/Libraries 29 | Requires: %{name} = %{version}-%{release}, libgcrypt-devel >= 1.2.0 30 | 31 | %description devel 32 | The devel package contains the libotr library and include files. 33 | 34 | %prep 35 | %setup -q 36 | 37 | %if %{snapshot} 38 | aclocal 39 | intltoolize --force --copy 40 | autoreconf -s -i 41 | %endif 42 | 43 | %build 44 | %configure --with-pic --disable-rpath --disable-static 45 | sed -i 's|^hardcode_libdir_flag_spec=.*|hardcode_libdir_flag_spec=""|g' libtool 46 | sed -i 's|^runpath_var=LD_RUN_PATH|runpath_var=DIE_RPATH_DIE|g' libtool 47 | make %{?_smp_mflags} all 48 | 49 | %install 50 | rm -rf $RPM_BUILD_ROOT 51 | make \ 52 | DESTDIR=$RPM_BUILD_ROOT \ 53 | LIBINSTDIR=%{_libdir} \ 54 | install 55 | rm -rf $RPM_BUILD_ROOT%{_libdir}/*.la 56 | 57 | %clean 58 | rm -rf $RPM_BUILD_ROOT 59 | 60 | %post -p /sbin/ldconfig 61 | 62 | %postun -p /sbin/ldconfig 63 | 64 | %files 65 | %defattr(-,root,root) 66 | %doc AUTHORS README COPYING COPYING.LIB NEWS Protocol* 67 | %{_libdir}/libotr.so.* 68 | %{_bindir}/* 69 | %{_mandir}/man1/* 70 | 71 | %files devel 72 | %defattr(-,root,root,-) 73 | %doc ChangeLog 74 | %{_libdir}/libotr.so 75 | %{_libdir}/pkgconfig/libotr.pc 76 | %dir %{_includedir}/libotr 77 | %{_includedir}/libotr/* 78 | %{_datadir}/aclocal/* 79 | 80 | 81 | %changelog 82 | * Sat Jul 27 2013 Paul Wouters - 4.0.0-1 83 | - Upgraded to libotr-4.0.0 84 | - Since this is API incompatible, there is also a libotr3 package 85 | 86 | * Thu Feb 14 2013 Fedora Release Engineering - 3.2.1-2 87 | - Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild 88 | 89 | * Wed Aug 15 2012 Paul Wouters - 3.2.1-1 90 | - Updated to 3.2.1, updates patch for rhbz#846377, CVE-2012-3461 91 | 92 | * Wed Aug 08 2012 Paul Wouters - 3.2.0-9 93 | - Patch for Multiple heap-based buffer overflows in the Base64 decoder 94 | (rhbz#846377, upstream will not release 3.2.1 for this) 95 | 96 | * Thu Jul 19 2012 Fedora Release Engineering - 3.2.0-8 97 | - Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild 98 | 99 | * Fri Jan 13 2012 Fedora Release Engineering - 3.2.0-7 100 | - Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild 101 | 102 | * Tue Feb 08 2011 Fedora Release Engineering - 3.2.0-6 103 | - Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild 104 | 105 | * Mon May 24 2010 Tom "spot" Callaway - 3.2.0-5 106 | - disable static libs 107 | - disable rpath 108 | 109 | * Fri Jul 24 2009 Fedora Release Engineering - 3.2.0-4 110 | - Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild 111 | 112 | * Wed Feb 25 2009 Fedora Release Engineering - 3.2.0-3 113 | - Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild 114 | 115 | * Thu Aug 7 2008 Tom "spot" Callaway - 3.2.0-2 116 | - fix license tag 117 | 118 | * Sun Jun 15 2008 Paul Wouters 3.2.0-1 119 | - Upgraded to 3.2.0 120 | 121 | * Tue Feb 19 2008 Fedora Release Engineering - 3.1.0-2 122 | - Autorebuild for GCC 4.3 123 | 124 | * Wed Aug 1 2007 Paul Wouters 3.1.0-1 125 | - Upgraded to current version 126 | - Updated URLS and configure line 127 | 128 | * Mon Sep 11 2006 Paul Wouters 3.0.0-2 129 | - Rebuild requested for PT_GNU_HASH support from gcc 130 | 131 | * Mon Oct 17 2005 Paul Wouters 3.0.0-1 132 | - Minor change to allow for new documentation files. Fixed Requires: 133 | 134 | * Sat Jun 19 2005 Paul Wouters 135 | - Fixed defattr, groups, description and duplicate files in devel 136 | 137 | * Fri Jun 17 2005 Tom "spot" Callaway 138 | - reworked for Fedora Extras 139 | 140 | * Tue May 3 2005 Ian Goldberg 141 | - Bumped version number to 2.0.2 142 | * Wed Feb 16 2005 Ian Goldberg 143 | - Bumped version number to 2.0.1 144 | * Tue Feb 8 2005 Ian Goldberg 145 | - Bumped version number to 2.0.0 146 | * Wed Feb 2 2005 Ian Goldberg 147 | - Added libotr.m4 to the devel package 148 | - Bumped version number to 1.99.0 149 | * Wed Jan 19 2005 Paul Wouters 150 | - Updated spec file for the gaim-otr libotr split 151 | * Tue Dec 21 2004 Ian Goldberg 152 | - Bumped to version 1.0.2. 153 | * Fri Dec 17 2004 Paul Wouters 154 | - instll fix for x86_64 155 | * Sun Dec 12 2004 Ian Goldberg 156 | - Bumped to version 1.0.0. 157 | * Fri Dec 10 2004 Ian Goldberg 158 | - Bumped to version 0.9.9rc2. 159 | * Thu Dec 9 2004 Ian Goldberg 160 | - Added CFLAGS to "make all", removed DESTDIR 161 | * Wed Dec 8 2004 Ian Goldberg 162 | - Bumped to version 0.9.9rc1. 163 | * Fri Dec 3 2004 Ian Goldberg 164 | - Bumped to version 0.9.1. 165 | * Wed Dec 1 2004 Paul Wouters 166 | - Bumped to version 0.9.0. 167 | - Fixed install for tools and cos 168 | - Added Obsoletes: target for otr-plugin so rpm-Uhv gaim-otr removes it. 169 | * Mon Nov 22 2004 Ian Goldberg 170 | - Bumped version to 0.8.1 171 | * Sun Nov 21 2004 Paul Wouters 172 | - Initial version 173 | 174 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CPPFLAGS = @LIBGCRYPT_CFLAGS@ 2 | 3 | lib_LTLIBRARIES = libotr.la 4 | 5 | libotr_la_SOURCES = privkey.c context.c proto.c b64.c dh.c mem.c message.c \ 6 | userstate.c tlv.c auth.c sm.c context_priv.c instag.c 7 | 8 | libotr_la_LDFLAGS = -version-info @LIBOTR_LIBTOOL_VERSION@ @LIBS@ @LIBGCRYPT_LIBS@ 9 | 10 | otrincdir = $(includedir)/libotr 11 | 12 | otrinc_HEADERS = b64.h context.h dh.h mem.h message.h privkey.h proto.h \ 13 | version.h userstate.h tlv.h serial.h auth.h sm.h privkey-t.h \ 14 | context_priv.h instag.h 15 | -------------------------------------------------------------------------------- /src/auth.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander, 4 | * Willy Lew, Lisa Du, Nikita Borisov 5 | * 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of version 2.1 of the GNU Lesser General 9 | * Public License as published by the Free Software Foundation. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #ifndef __AUTH_H__ 22 | #define __AUTH_H__ 23 | 24 | #include 25 | #include 26 | #include "dh.h" 27 | 28 | 29 | typedef enum { 30 | OTRL_AUTHSTATE_NONE, 31 | OTRL_AUTHSTATE_AWAITING_DHKEY, 32 | OTRL_AUTHSTATE_AWAITING_REVEALSIG, 33 | OTRL_AUTHSTATE_AWAITING_SIG, 34 | OTRL_AUTHSTATE_V1_SETUP 35 | } OtrlAuthState; 36 | 37 | typedef struct { 38 | OtrlAuthState authstate; /* Our state */ 39 | 40 | struct context *context; /* The context which points to us */ 41 | 42 | DH_keypair our_dh; /* Our D-H key */ 43 | unsigned int our_keyid; /* ...and its keyid */ 44 | 45 | unsigned char *encgx; /* The encrypted value of g^x */ 46 | size_t encgx_len; /* ...and its length */ 47 | unsigned char r[16]; /* The encryption key */ 48 | 49 | unsigned char hashgx[32]; /* SHA256(g^x) */ 50 | 51 | gcry_mpi_t their_pub; /* Their D-H public key */ 52 | unsigned int their_keyid; /* ...and its keyid */ 53 | 54 | 55 | gcry_cipher_hd_t enc_c, enc_cp; /* c and c' encryption keys */ 56 | gcry_md_hd_t mac_m1, mac_m1p; /* m1 and m1' MAC keys */ 57 | gcry_md_hd_t mac_m2, mac_m2p; /* m2 and m2' MAC keys */ 58 | 59 | unsigned char their_fingerprint[20]; /* The fingerprint of their 60 | long-term signing key */ 61 | 62 | int initiated; /* Did we initiate this 63 | authentication? */ 64 | 65 | unsigned int protocol_version; /* The protocol version number 66 | used to authenticate. */ 67 | 68 | unsigned char secure_session_id[20]; /* The secure session id */ 69 | size_t secure_session_id_len; /* And its actual length, 70 | which may be either 20 (for 71 | v1) or 8 (for v2) */ 72 | OtrlSessionIdHalf session_id_half; /* Which half of the session 73 | id gets shown in bold */ 74 | 75 | char *lastauthmsg; /* The last auth message 76 | (base-64 encoded) we sent, 77 | in case we need to 78 | retransmit it. */ 79 | 80 | time_t commit_sent_time; /* The time we last sent the 81 | lastauthmsg, if it was a 82 | COMMIT message, and this is 83 | a master context. 0 84 | otherwise. */ 85 | } OtrlAuthInfo; 86 | 87 | #include "privkey-t.h" 88 | 89 | /* 90 | * Initialize the fields of an OtrlAuthInfo (already allocated). 91 | */ 92 | void otrl_auth_new(struct context *context); 93 | 94 | /* 95 | * Clear the fields of an OtrlAuthInfo (but leave it allocated). 96 | */ 97 | void otrl_auth_clear(OtrlAuthInfo *auth); 98 | 99 | /* 100 | * Start a fresh AKE (version 2 or 3) using the given OtrlAuthInfo. Generate 101 | * a fresh DH keypair to use. If no error is returned, the message to 102 | * transmit will be contained in auth->lastauthmsg. 103 | */ 104 | gcry_error_t otrl_auth_start_v23(OtrlAuthInfo *auth, int version); 105 | 106 | /* 107 | * Handle an incoming D-H Commit Message. If no error is returned, the 108 | * message to send will be left in auth->lastauthmsg. Generate a fresh 109 | * keypair to use. 110 | */ 111 | gcry_error_t otrl_auth_handle_commit(OtrlAuthInfo *auth, 112 | const char *commitmsg, int version); 113 | 114 | /* 115 | * Handle an incoming D-H Key Message. If no error is returned, and 116 | * *havemsgp is 1, the message to sent will be left in auth->lastauthmsg. 117 | * Use the given private authentication key to sign messages. 118 | */ 119 | gcry_error_t otrl_auth_handle_key(OtrlAuthInfo *auth, const char *keymsg, 120 | int *havemsgp, OtrlPrivKey *privkey); 121 | 122 | /* 123 | * Handle an incoming Reveal Signature Message. If no error is 124 | * returned, and *havemsgp is 1, the message to be sent will be left in 125 | * auth->lastauthmsg. Use the given private authentication key to sign 126 | * messages. Call the auth_succeeded callback if authentication is 127 | * successful. 128 | */ 129 | gcry_error_t otrl_auth_handle_revealsig(OtrlAuthInfo *auth, 130 | const char *revealmsg, int *havemsgp, OtrlPrivKey *privkey, 131 | gcry_error_t (*auth_succeeded)(const OtrlAuthInfo *auth, void *asdata), 132 | void *asdata); 133 | 134 | /* 135 | * Handle an incoming Signature Message. If no error is returned, and 136 | * *havemsgp is 1, the message to be sent will be left in 137 | * auth->lastauthmsg. Call the auth_succeeded callback if 138 | * authentication is successful. 139 | */ 140 | gcry_error_t otrl_auth_handle_signature(OtrlAuthInfo *auth, 141 | const char *sigmsg, int *havemsgp, 142 | gcry_error_t (*auth_succeeded)(const OtrlAuthInfo *auth, void *asdata), 143 | void *asdata); 144 | 145 | /* 146 | * Start a fresh AKE (version 1) using the given OtrlAuthInfo. If 147 | * our_dh is NULL, generate a fresh DH keypair to use. Otherwise, use a 148 | * copy of the one passed (with the given keyid). Use the given private 149 | * key to sign the message. If no error is returned, the message to 150 | * transmit will be contained in auth->lastauthmsg. 151 | */ 152 | gcry_error_t otrl_auth_start_v1(OtrlAuthInfo *auth, DH_keypair *our_dh, 153 | unsigned int our_keyid, OtrlPrivKey *privkey); 154 | 155 | /* 156 | * Handle an incoming v1 Key Exchange Message. If no error is returned, 157 | * and *havemsgp is 1, the message to be sent will be left in 158 | * auth->lastauthmsg. Use the given private authentication key to sign 159 | * messages. Call the auth_secceeded callback if authentication is 160 | * successful. If non-NULL, use a copy of the given D-H keypair, with 161 | * the given keyid. 162 | */ 163 | gcry_error_t otrl_auth_handle_v1_key_exchange(OtrlAuthInfo *auth, 164 | const char *keyexchmsg, int *havemsgp, OtrlPrivKey *privkey, 165 | DH_keypair *our_dh, unsigned int our_keyid, 166 | gcry_error_t (*auth_succeeded)(const OtrlAuthInfo *auth, void *asdata), 167 | void *asdata); 168 | 169 | /* 170 | * Copy relevant information from the master OtrlAuthInfo to an 171 | * instance OtrlAuthInfo in response to a D-H Key with a new 172 | * instance. The fields copied will depend on the state of the 173 | * master auth. 174 | */ 175 | void otrl_auth_copy_on_key(OtrlAuthInfo *m_auth, OtrlAuthInfo *auth); 176 | 177 | #endif 178 | -------------------------------------------------------------------------------- /src/b64.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew, 4 | * Nikita Borisov 5 | * 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of version 2.1 of the GNU Lesser General 9 | * Public License as published by the Free Software Foundation. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | /* Modified from: */ 22 | 23 | /*********************************************************************\ 24 | 25 | MODULE NAME: b64.c 26 | 27 | AUTHOR: Bob Trower 08/04/01 28 | 29 | LICENCE: Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc. 30 | 31 | Permission is hereby granted, free of charge, to any person 32 | obtaining a copy of this software and associated 33 | documentation files (the "Software"), to deal in the 34 | Software without restriction, including without limitation 35 | the rights to use, copy, modify, merge, publish, distribute, 36 | sublicense, and/or sell copies of the Software, and to 37 | permit persons to whom the Software is furnished to do so, 38 | subject to the following conditions: 39 | 40 | The above copyright notice and this permission notice shall 41 | be included in all copies or substantial portions of the 42 | Software. 43 | 44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 45 | KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 46 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 47 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 48 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 49 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 50 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 51 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 52 | 53 | VERSION HISTORY: 54 | Bob Trower 08/04/01 -- Create Version 0.00.00B 55 | 56 | \******************************************************************* */ 57 | 58 | /* system headers */ 59 | #include 60 | #include 61 | 62 | /* libotr headers */ 63 | #include "b64.h" 64 | 65 | /* 66 | ** Translation Table as described in RFC1113 67 | */ 68 | static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 69 | 70 | /* 71 | ** Translation Table to decode (created by author) 72 | */ 73 | static const char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq"; 74 | 75 | /* 76 | ** encodeblock 77 | ** 78 | ** encode up to 3 8-bit binary bytes as 4 '6-bit' characters. 79 | ** len must be 1, 2, or 3. 80 | */ 81 | static void encodeblock( char *out, const unsigned char *in, size_t len ) 82 | { 83 | unsigned char in0, in1, in2; 84 | in0 = in[0]; 85 | in1 = len > 1 ? in[1] : 0; 86 | in2 = len > 2 ? in[2] : 0; 87 | 88 | out[0] = cb64[ in0 >> 2 ]; 89 | out[1] = cb64[ ((in0 & 0x03) << 4) | ((in1 & 0xf0) >> 4) ]; 90 | out[2] = len > 1 ? cb64[ ((in1 & 0x0f) << 2) | ((in2 & 0xc0) >> 6) ] 91 | : '='; 92 | out[3] = len > 2 ? cb64[ in2 & 0x3f ] 93 | : '='; 94 | } 95 | 96 | /* 97 | * base64 encode data. Insert no linebreaks or whitespace. 98 | * 99 | * The buffer base64data must contain at least ((datalen+2)/3)*4 bytes of 100 | * space. This function will return the number of bytes actually used. 101 | */ 102 | size_t otrl_base64_encode(char *base64data, const unsigned char *data, 103 | size_t datalen) 104 | { 105 | size_t base64len = 0; 106 | 107 | while(datalen > 2) { 108 | encodeblock(base64data, data, 3); 109 | base64data += 4; 110 | base64len += 4; 111 | data += 3; 112 | datalen -= 3; 113 | } 114 | if (datalen > 0) { 115 | encodeblock(base64data, data, datalen); 116 | base64len += 4; 117 | } 118 | 119 | return base64len; 120 | } 121 | 122 | static size_t decode(unsigned char *out, const char *in, size_t b64len) 123 | { 124 | size_t written = 0; 125 | unsigned char c = 0; 126 | 127 | if (b64len > 0) { 128 | c = in[0] << 2; 129 | } 130 | if (b64len > 1) { 131 | out[0] = c | in[1] >> 4; 132 | written = 1; 133 | c = in[1] << 4; 134 | } 135 | if (b64len > 2) { 136 | out[1] = c | in[2] >> 2; 137 | written = 2; 138 | c = in[2] << 6; 139 | } 140 | if (b64len > 3) { 141 | out[2] = c | in[3]; 142 | written = 3; 143 | } 144 | return written; 145 | } 146 | 147 | /* 148 | * base64 decode data. Skip non-base64 chars, and terminate at the 149 | * first '=', or the end of the buffer. 150 | * 151 | * The buffer data must contain at least ((base64len+3) / 4) * 3 bytes 152 | * of space. This function will return the number of bytes actually 153 | * used. 154 | */ 155 | size_t otrl_base64_decode(unsigned char *data, const char *base64data, 156 | size_t base64len) 157 | { 158 | size_t datalen = 0; 159 | char b64[4]; 160 | size_t b64accum = 0; 161 | 162 | while(base64len > 0) { 163 | char b = *base64data; 164 | unsigned char bdecode; 165 | ++base64data; 166 | --base64len; 167 | if (b < '+' || b > 'z') continue; /* Skip non-base64 chars */ 168 | if (b == '=') { 169 | /* Force termination */ 170 | datalen += decode(data, b64, b64accum); 171 | base64len = 0; 172 | } else { 173 | bdecode = cd64[b-'+']; 174 | if (bdecode == '$') continue; /* Skip non-base64 chars */ 175 | b64[b64accum++] = bdecode-'>'; 176 | if (b64accum == 4) { 177 | /* We have a complete block; decode it. */ 178 | size_t written = decode(data, b64, b64accum); 179 | data += written; 180 | datalen += written; 181 | b64accum = 0; 182 | } 183 | } 184 | } 185 | 186 | /* Just discard any short block at the end. */ 187 | 188 | return datalen; 189 | } 190 | 191 | /* 192 | * Base64-encode a block of data, stick "?OTR:" and "." around it, and 193 | * return the result, or NULL in the event of a memory error. The 194 | * caller must free() the return value. 195 | */ 196 | char *otrl_base64_otr_encode(const unsigned char *buf, size_t buflen) 197 | { 198 | char *base64buf; 199 | size_t base64len; 200 | 201 | /* Make the base64-encoding. */ 202 | base64len = ((buflen + 2) / 3) * 4; 203 | base64buf = malloc(5 + base64len + 1 + 1); 204 | if (base64buf == NULL) { 205 | return NULL; 206 | } 207 | memmove(base64buf, "?OTR:", 5); 208 | otrl_base64_encode(base64buf+5, buf, buflen); 209 | base64buf[5 + base64len] = '.'; 210 | base64buf[5 + base64len + 1] = '\0'; 211 | 212 | return base64buf; 213 | } 214 | 215 | /* 216 | * Base64-decode the portion of the given message between "?OTR:" and 217 | * ".". Set *bufp to the decoded data, and set *lenp to its length. 218 | * The caller must free() the result. Return 0 on success, -1 on a 219 | * memory error, or -2 on invalid input. 220 | */ 221 | int otrl_base64_otr_decode(const char *msg, unsigned char **bufp, 222 | size_t *lenp) 223 | { 224 | char *otrtag, *endtag; 225 | size_t msglen, rawlen; 226 | unsigned char *rawmsg; 227 | 228 | otrtag = strstr(msg, "?OTR:"); 229 | if (!otrtag) { 230 | return -2; 231 | } 232 | 233 | endtag = strchr(otrtag, '.'); 234 | if (endtag) { 235 | msglen = endtag-otrtag; 236 | } else { 237 | return -2; 238 | } 239 | 240 | /* Skip over the "?OTR:" */ 241 | otrtag += 5; 242 | msglen -= 5; 243 | 244 | /* Base64-decode the message */ 245 | rawlen = OTRL_B64_MAX_DECODED_SIZE(msglen); /* maximum possible */ 246 | rawmsg = malloc(rawlen); 247 | if (!rawmsg && rawlen > 0) { 248 | return -1; 249 | } 250 | 251 | rawlen = otrl_base64_decode(rawmsg, otrtag, msglen); /* actual size */ 252 | 253 | *bufp = rawmsg; 254 | *lenp = rawlen; 255 | 256 | return 0; 257 | } 258 | -------------------------------------------------------------------------------- /src/b64.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew, 4 | * Nikita Borisov 5 | * 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of version 2.1 of the GNU Lesser General 9 | * Public License as published by the Free Software Foundation. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #ifndef __B64_H__ 22 | #define __B64_H__ 23 | 24 | #include 25 | 26 | /* Base64 encodes blocks of this many bytes: */ 27 | #define OTRL_B64_DECODED_LEN 3 28 | /* into blocks of this many bytes: */ 29 | #define OTRL_B64_ENCODED_LEN 4 30 | 31 | /* An encoded block of length encoded_len can turn into a maximum of 32 | * this many decoded bytes: */ 33 | #define OTRL_B64_MAX_DECODED_SIZE(encoded_len) \ 34 | (((encoded_len + OTRL_B64_ENCODED_LEN - 1) / OTRL_B64_ENCODED_LEN) \ 35 | * OTRL_B64_DECODED_LEN) 36 | 37 | /* 38 | * base64 encode data. Insert no linebreaks or whitespace. 39 | * 40 | * The buffer base64data must contain at least ((datalen+2)/3)*4 bytes of 41 | * space. This function will return the number of bytes actually used. 42 | */ 43 | size_t otrl_base64_encode(char *base64data, const unsigned char *data, 44 | size_t datalen); 45 | 46 | /* 47 | * base64 decode data. Skip non-base64 chars, and terminate at the 48 | * first '=', or the end of the buffer. 49 | * 50 | * The buffer data must contain at least ((base64len+3) / 4) * 3 bytes 51 | * of space. This function will return the number of bytes actually 52 | * used. 53 | */ 54 | size_t otrl_base64_decode(unsigned char *data, const char *base64data, 55 | size_t base64len); 56 | 57 | /* 58 | * Base64-encode a block of data, stick "?OTR:" and "." around it, and 59 | * return the result, or NULL in the event of a memory error. 60 | */ 61 | char *otrl_base64_otr_encode(const unsigned char *buf, size_t buflen); 62 | 63 | /* 64 | * Base64-decode the portion of the given message between "?OTR:" and 65 | * ".". Set *bufp to the decoded data, and set *lenp to its length. 66 | * The caller must free() the result. Return 0 on success, -1 on a 67 | * memory error, or -2 on invalid input. 68 | */ 69 | int otrl_base64_otr_decode(const char *msg, unsigned char **bufp, 70 | size_t *lenp); 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /src/context.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2014 Ian Goldberg, David Goulet, Rob Smits, 4 | * Chris Alexander, Willy Lew, Lisa Du, 5 | * Nikita Borisov 6 | * 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of version 2.1 of the GNU Lesser General 10 | * Public License as published by the Free Software Foundation. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public 18 | * License along with this library; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | 22 | #ifndef __CONTEXT_H__ 23 | #define __CONTEXT_H__ 24 | 25 | #include "context_priv.h" 26 | 27 | #include 28 | 29 | #include "dh.h" 30 | #include "auth.h" 31 | #include "sm.h" 32 | 33 | typedef struct context ConnContext; /* Forward declare */ 34 | 35 | #include "instag.h" 36 | 37 | typedef enum { 38 | OTRL_MSGSTATE_PLAINTEXT, /* Not yet started an encrypted 39 | conversation */ 40 | OTRL_MSGSTATE_ENCRYPTED, /* Currently in an encrypted 41 | conversation */ 42 | OTRL_MSGSTATE_FINISHED /* The remote side has sent us a 43 | notification that he has ended 44 | his end of the encrypted 45 | conversation; prevent any 46 | further messages from being 47 | sent to him. */ 48 | } OtrlMessageState; 49 | 50 | typedef struct s_fingerprint { 51 | struct s_fingerprint *next; /* The next fingerprint in the list */ 52 | struct s_fingerprint **tous; /* A pointer to the pointer to us */ 53 | unsigned char *fingerprint; /* The fingerprint, or NULL */ 54 | struct context *context; /* The context to which we belong */ 55 | char *trust; /* The trust level of the fingerprint */ 56 | } Fingerprint; 57 | 58 | struct context { 59 | struct context * next; /* Linked list pointer */ 60 | struct context ** tous; /* A pointer to the pointer to us */ 61 | 62 | /* Context information that is meant for internal use */ 63 | 64 | ConnContextPriv *context_priv; 65 | 66 | /* Context information that is meant for application use */ 67 | 68 | char * username; /* The user this context is for */ 69 | char * accountname; /* The username is relative to 70 | this account... */ 71 | char * protocol; /* ... and this protocol */ 72 | 73 | struct context *m_context; /* If this is a child context, this 74 | field will point to the master 75 | context. Otherwise it will point to 76 | itself. */ 77 | struct context *recent_rcvd_child; /* If this is a master context, this 78 | points to the child context that 79 | has received a message most recently. 80 | By default, it will point to the 81 | master context. In child contexts 82 | this field is NULL. */ 83 | struct context *recent_sent_child; /* Similar to above, but it points to 84 | the child who has sent most 85 | recently. */ 86 | struct context *recent_child; /* Similar to above, but will point to 87 | the most recent of recent_rcvd_child 88 | and recent_sent_child */ 89 | 90 | otrl_instag_t our_instance; /* Our instance tag for this computer*/ 91 | otrl_instag_t their_instance; /* The user's instance tag */ 92 | 93 | OtrlMessageState msgstate; /* The state of message disposition 94 | with this user */ 95 | OtrlAuthInfo auth; /* The state of ongoing 96 | authentication with this user */ 97 | 98 | Fingerprint fingerprint_root; /* The root of a linked list of 99 | Fingerprints entries. This list will 100 | only be populated in master contexts. 101 | For child contexts, 102 | fingerprint_root.next will always 103 | point to NULL. */ 104 | Fingerprint *active_fingerprint; /* Which fingerprint is in use now? 105 | A pointer into the above list */ 106 | 107 | unsigned char sessionid[20]; /* The sessionid and bold half */ 108 | size_t sessionid_len; /* determined when this private */ 109 | OtrlSessionIdHalf sessionid_half; /* connection was established. */ 110 | 111 | unsigned int protocol_version; /* The version of OTR in use */ 112 | 113 | enum { 114 | OFFER_NOT, 115 | OFFER_SENT, 116 | OFFER_REJECTED, 117 | OFFER_ACCEPTED 118 | } otr_offer; /* Has this correspondent repsponded to our 119 | OTR offers? */ 120 | 121 | /* Application data to be associated with this context */ 122 | void *app_data; 123 | /* A function to free the above data when we forget this context */ 124 | void (*app_data_free)(void *); 125 | 126 | OtrlSMState *smstate; /* The state of the current 127 | socialist millionaires exchange */ 128 | }; 129 | 130 | #include "userstate.h" 131 | 132 | /* Look up a connection context by name/account/protocol/instance from the 133 | * given OtrlUserState. If add_if_missing is true, allocate and return a 134 | * new context if one does not currently exist. In that event, call 135 | * add_app_data(data, context) so that app_data and app_data_free can be 136 | * filled in by the application, and set *addedp to 1. 137 | * In the 'their_instance' field note that you can also specify a 'meta- 138 | * instance' value such as OTRL_INSTAG_MASTER, OTRL_INSTAL_RECENT, 139 | * OTRL_INSTAG_RECENT_RECEIVED and OTRL_INSTAG_RECENT_SENT. */ 140 | ConnContext * otrl_context_find(OtrlUserState us, const char *user, 141 | const char *accountname, const char *protocol, 142 | otrl_instag_t their_instance, int add_if_missing, int *addedp, 143 | void (*add_app_data)(void *data, ConnContext *context), void *data); 144 | 145 | /* Return true iff the given fingerprint is marked as trusted. */ 146 | int otrl_context_is_fingerprint_trusted(Fingerprint *fprint); 147 | 148 | /* This method gets called after sending or receiving a message, to 149 | * update the master context's "recent context" pointers. */ 150 | void otrl_context_update_recent_child(ConnContext *context, 151 | unsigned int sent_msg); 152 | 153 | /* Find a fingerprint in a given context, perhaps adding it if not 154 | * present. */ 155 | Fingerprint *otrl_context_find_fingerprint(ConnContext *context, 156 | unsigned char fingerprint[20], int add_if_missing, int *addedp); 157 | 158 | /* Set the trust level for a given fingerprint */ 159 | void otrl_context_set_trust(Fingerprint *fprint, const char *trust); 160 | 161 | /* Force a context into the OTRL_MSGSTATE_FINISHED state. */ 162 | void otrl_context_force_finished(ConnContext *context); 163 | 164 | /* Force a context into the OTRL_MSGSTATE_PLAINTEXT state. */ 165 | void otrl_context_force_plaintext(ConnContext *context); 166 | 167 | /* Forget a fingerprint (so long as it's not the active one. If it's a 168 | * fingerprint_root, forget the whole context (as long as 169 | * and_maybe_context is set, and it's PLAINTEXT). Also, if it's not 170 | * the fingerprint_root, but it's the only fingerprint, and we're 171 | * PLAINTEXT, forget the whole context if and_maybe_context is set. */ 172 | void otrl_context_forget_fingerprint(Fingerprint *fprint, 173 | int and_maybe_context); 174 | 175 | /* Forget a whole context, so long as it's PLAINTEXT. If a context has child 176 | * instances, don't remove this instance unless children are also all in 177 | * PLAINTEXT state. In this case, the children will also be removed. 178 | * Returns 0 on success, 1 on failure. */ 179 | int otrl_context_forget(ConnContext *context); 180 | 181 | /* Forget all the contexts in a given OtrlUserState. */ 182 | void otrl_context_forget_all(OtrlUserState us); 183 | 184 | /* Find requested recent instance */ 185 | ConnContext * otrl_context_find_recent_instance(ConnContext * context, 186 | otrl_instag_t recent_instag); 187 | 188 | /* Find the instance of this context that has the best security level, and for 189 | * which we have most recently received a message from. Note that most recent 190 | * in this case is limited to a one-second resolution. */ 191 | ConnContext * otrl_context_find_recent_secure_instance(ConnContext * context); 192 | 193 | #endif 194 | -------------------------------------------------------------------------------- /src/context_priv.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew, 4 | * Nikita Borisov 5 | * 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of version 2.1 of the GNU Lesser General 9 | * Public License as published by the Free Software Foundation. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | /* system headers */ 22 | #include 23 | #include 24 | 25 | /* libgcrypt headers */ 26 | #include 27 | 28 | /* libotr headers */ 29 | #include "context_priv.h" 30 | 31 | /* Create a new private connection context */ 32 | ConnContextPriv *otrl_context_priv_new() 33 | { 34 | ConnContextPriv *context_priv; 35 | context_priv = malloc(sizeof(*context_priv)); 36 | assert(context_priv != NULL); 37 | 38 | context_priv->fragment = NULL; 39 | context_priv->fragment_len = 0; 40 | context_priv->fragment_n = 0; 41 | context_priv->fragment_k = 0; 42 | context_priv->numsavedkeys = 0; 43 | context_priv->saved_mac_keys = NULL; 44 | context_priv->generation = 0; 45 | context_priv->lastsent = 0; 46 | context_priv->lastmessage = NULL; 47 | context_priv->lastrecv = 0; 48 | context_priv->may_retransmit = 0; 49 | context_priv->their_keyid = 0; 50 | context_priv->their_y = NULL; 51 | context_priv->their_old_y = NULL; 52 | context_priv->our_keyid = 0; 53 | context_priv->our_dh_key.groupid = 0; 54 | context_priv->our_dh_key.priv = NULL; 55 | context_priv->our_dh_key.pub = NULL; 56 | context_priv->our_old_dh_key.groupid = 0; 57 | context_priv->our_old_dh_key.priv = NULL; 58 | context_priv->our_old_dh_key.pub = NULL; 59 | otrl_dh_session_blank(&(context_priv->sesskeys[0][0])); 60 | otrl_dh_session_blank(&(context_priv->sesskeys[0][1])); 61 | otrl_dh_session_blank(&(context_priv->sesskeys[1][0])); 62 | otrl_dh_session_blank(&(context_priv->sesskeys[1][1])); 63 | 64 | return context_priv; 65 | } 66 | 67 | /* Resets the appropriate variables when a context 68 | * is being force finished 69 | */ 70 | void otrl_context_priv_force_finished(ConnContextPriv *context_priv) 71 | { 72 | free(context_priv->fragment); 73 | context_priv->fragment = NULL; 74 | context_priv->fragment_len = 0; 75 | context_priv->fragment_n = 0; 76 | context_priv->fragment_k = 0; 77 | context_priv->numsavedkeys = 0; 78 | free(context_priv->saved_mac_keys); 79 | context_priv->saved_mac_keys = NULL; 80 | gcry_free(context_priv->lastmessage); 81 | context_priv->lastmessage = NULL; 82 | context_priv->may_retransmit = 0; 83 | context_priv->their_keyid = 0; 84 | gcry_mpi_release(context_priv->their_y); 85 | context_priv->their_y = NULL; 86 | gcry_mpi_release(context_priv->their_old_y); 87 | context_priv->their_old_y = NULL; 88 | context_priv->our_keyid = 0; 89 | otrl_dh_keypair_free(&(context_priv->our_dh_key)); 90 | otrl_dh_keypair_free(&(context_priv->our_old_dh_key)); 91 | otrl_dh_session_free(&(context_priv->sesskeys[0][0])); 92 | otrl_dh_session_free(&(context_priv->sesskeys[0][1])); 93 | otrl_dh_session_free(&(context_priv->sesskeys[1][0])); 94 | otrl_dh_session_free(&(context_priv->sesskeys[1][1])); 95 | } 96 | -------------------------------------------------------------------------------- /src/context_priv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew, 4 | * Lisa Du, Nikita Borisov 5 | * 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of version 2.1 of the GNU Lesser General 9 | * Public License as published by the Free Software Foundation. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #ifndef __CONTEXT_PRIV_H__ 22 | #define __CONTEXT_PRIV_H__ 23 | 24 | #include 25 | 26 | #include "dh.h" 27 | #include "auth.h" 28 | #include "sm.h" 29 | 30 | typedef struct context_priv { 31 | /* The part of the fragmented message we've seen so far */ 32 | char *fragment; 33 | 34 | /* The length of fragment */ 35 | size_t fragment_len; 36 | 37 | /* The total number of fragments in this message */ 38 | unsigned short fragment_n; 39 | 40 | /* The highest fragment number we've seen so far for this message */ 41 | unsigned short fragment_k; 42 | 43 | /* current keyid used by other side; this is set to 0 if we get 44 | * a OTRL_TLV_DISCONNECTED message from them. */ 45 | unsigned int their_keyid; 46 | 47 | /* Y[their_keyid] (their DH pubkey) */ 48 | gcry_mpi_t their_y; 49 | 50 | /* Y[their_keyid-1] (their prev DH pubkey) */ 51 | gcry_mpi_t their_old_y; 52 | 53 | /* current keyid used by us */ 54 | unsigned int our_keyid; 55 | 56 | /* DH key[our_keyid] */ 57 | DH_keypair our_dh_key; 58 | 59 | /* DH key[our_keyid-1] */ 60 | DH_keypair our_old_dh_key; 61 | 62 | /* sesskeys[i][j] are the session keys derived from DH 63 | * key[our_keyid-i] and mpi Y[their_keyid-j] */ 64 | DH_sesskeys sesskeys[2][2]; 65 | 66 | /* saved mac keys to be revealed later */ 67 | unsigned int numsavedkeys; 68 | unsigned char *saved_mac_keys; 69 | 70 | /* generation number: increment every time we go private, and never 71 | * reset to 0 (unless we remove the context entirely) */ 72 | unsigned int generation; 73 | 74 | /* The last time a Data Message was sent */ 75 | time_t lastsent; 76 | 77 | /* The last time a Data Message was received */ 78 | time_t lastrecv; 79 | 80 | /* The plaintext of the last Data Message sent */ 81 | char *lastmessage; 82 | 83 | /* Is the last message eligible for retransmission? */ 84 | int may_retransmit; 85 | 86 | } ConnContextPriv; 87 | 88 | /* Create a new private connection context. */ 89 | ConnContextPriv *otrl_context_priv_new(); 90 | 91 | /* Frees up memory that was used in otrl_context_priv_new */ 92 | void otrl_context_priv_force_finished(ConnContextPriv *context_priv); 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /src/dh.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew, 4 | * Nikita Borisov 5 | * 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of version 2.1 of the GNU Lesser General 9 | * Public License as published by the Free Software Foundation. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #ifndef __DH_H__ 22 | #define __DH_H__ 23 | 24 | #define DH1536_GROUP_ID 5 25 | 26 | typedef struct { 27 | unsigned int groupid; 28 | gcry_mpi_t priv, pub; 29 | } DH_keypair; 30 | 31 | /* Which half of the secure session id should be shown in bold? */ 32 | typedef enum { 33 | OTRL_SESSIONID_FIRST_HALF_BOLD, 34 | OTRL_SESSIONID_SECOND_HALF_BOLD 35 | } OtrlSessionIdHalf; 36 | 37 | #define OTRL_EXTRAKEY_BYTES 32 38 | 39 | typedef struct { 40 | unsigned char sendctr[16]; 41 | unsigned char rcvctr[16]; 42 | gcry_cipher_hd_t sendenc; 43 | gcry_cipher_hd_t rcvenc; 44 | gcry_md_hd_t sendmac; 45 | unsigned char sendmackey[20]; 46 | int sendmacused; 47 | gcry_md_hd_t rcvmac; 48 | unsigned char rcvmackey[20]; 49 | int rcvmacused; 50 | unsigned char extrakey[OTRL_EXTRAKEY_BYTES]; 51 | } DH_sesskeys; 52 | 53 | /* 54 | * Call this once, at plugin load time. It sets up the modulus and 55 | * generator MPIs. 56 | */ 57 | void otrl_dh_init(void); 58 | 59 | /* 60 | * Initialize the fields of a DH keypair. 61 | */ 62 | void otrl_dh_keypair_init(DH_keypair *kp); 63 | 64 | /* 65 | * Copy a DH_keypair. 66 | */ 67 | void otrl_dh_keypair_copy(DH_keypair *dst, const DH_keypair *src); 68 | 69 | /* 70 | * Deallocate the contents of a DH_keypair (but not the DH_keypair 71 | * itself) 72 | */ 73 | void otrl_dh_keypair_free(DH_keypair *kp); 74 | 75 | /* 76 | * Generate a DH keypair for a specified group. 77 | */ 78 | gcry_error_t otrl_dh_gen_keypair(unsigned int groupid, DH_keypair *kp); 79 | 80 | /* 81 | * Construct session keys from a DH keypair and someone else's public 82 | * key. 83 | */ 84 | gcry_error_t otrl_dh_session(DH_sesskeys *sess, const DH_keypair *kp, 85 | gcry_mpi_t y); 86 | 87 | /* 88 | * Compute the secure session id, two encryption keys, and four MAC keys 89 | * given our DH key and their DH public key. 90 | */ 91 | gcry_error_t otrl_dh_compute_v2_auth_keys(const DH_keypair *our_dh, 92 | gcry_mpi_t their_pub, unsigned char *sessionid, size_t *sessionidlenp, 93 | gcry_cipher_hd_t *enc_c, gcry_cipher_hd_t *enc_cp, 94 | gcry_md_hd_t *mac_m1, gcry_md_hd_t *mac_m1p, 95 | gcry_md_hd_t *mac_m2, gcry_md_hd_t *mac_m2p); 96 | 97 | /* 98 | * Compute the secure session id, given our DH key and their DH public 99 | * key. 100 | */ 101 | gcry_error_t otrl_dh_compute_v1_session_id(const DH_keypair *our_dh, 102 | gcry_mpi_t their_pub, unsigned char *sessionid, size_t *sessionidlenp, 103 | OtrlSessionIdHalf *halfp); 104 | 105 | /* 106 | * Deallocate the contents of a DH_sesskeys (but not the DH_sesskeys 107 | * itself) 108 | */ 109 | void otrl_dh_session_free(DH_sesskeys *sess); 110 | 111 | /* 112 | * Blank out the contents of a DH_sesskeys (without releasing it) 113 | */ 114 | void otrl_dh_session_blank(DH_sesskeys *sess); 115 | 116 | /* Increment the top half of a counter block */ 117 | void otrl_dh_incctr(unsigned char *ctr); 118 | 119 | /* Compare two counter values (8 bytes each). Return 0 if ctr1 == ctr2, 120 | * < 0 if ctr1 < ctr2 (as unsigned 64-bit values), > 0 if ctr1 > ctr2. */ 121 | int otrl_dh_cmpctr(const unsigned char *ctr1, const unsigned char *ctr2); 122 | 123 | #endif 124 | -------------------------------------------------------------------------------- /src/instag.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander, 4 | * Willy Lew, Lisa Du, Nikita Borisov 5 | * 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of version 2.1 of the GNU Lesser General 9 | * Public License as published by the Free Software Foundation. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | /* system headers */ 22 | #include 23 | #include 24 | 25 | /* libgcrypt headers */ 26 | #include 27 | 28 | /* libotr headers */ 29 | #include "instag.h" 30 | #include "userstate.h" 31 | 32 | /* Forget the given instag. */ 33 | void otrl_instag_forget(OtrlInsTag* instag) { 34 | if (!instag) return; 35 | 36 | if (instag->accountname) free(instag->accountname); 37 | if (instag->protocol) free(instag->protocol); 38 | 39 | /* Re-link the list */ 40 | *(instag->tous) = instag->next; 41 | if (instag->next) { 42 | instag->next->tous = instag->tous; 43 | } 44 | 45 | free(instag); 46 | } 47 | 48 | /* Forget all instags in a given OtrlUserState. */ 49 | void otrl_instag_forget_all(OtrlUserState us) { 50 | while(us->instag_root) { 51 | otrl_instag_forget(us->instag_root); 52 | } 53 | } 54 | 55 | /* Fetch the instance tag from the given OtrlUserState associated with 56 | * the given account */ 57 | OtrlInsTag * otrl_instag_find(OtrlUserState us, const char *accountname, 58 | const char *protocol) 59 | { 60 | OtrlInsTag *p; 61 | 62 | for(p=us->instag_root; p; p=p->next) { 63 | if (!strcmp(p->accountname, accountname) && 64 | !strcmp(p->protocol, protocol)) { 65 | return p; 66 | } 67 | } 68 | return NULL; 69 | } 70 | 71 | /* Read our instance tag from a file on disk into the given 72 | * OtrlUserState. */ 73 | gcry_error_t otrl_instag_read(OtrlUserState us, const char *filename) 74 | { 75 | gcry_error_t err; 76 | FILE *instf; 77 | 78 | /* Open the instance tag file. */ 79 | instf = fopen(filename, "rb"); 80 | if (!instf) { 81 | return gcry_error_from_errno(errno); 82 | } 83 | 84 | err = otrl_instag_read_FILEp(us, instf); 85 | fclose(instf); 86 | return err; 87 | } 88 | 89 | /* Read our instance tag from a file on disk into the given 90 | * OtrlUserState. The FILE* must be open for reading. */ 91 | gcry_error_t otrl_instag_read_FILEp(OtrlUserState us, FILE *instf) 92 | { 93 | if (!instf) return gcry_error(GPG_ERR_NO_ERROR); 94 | 95 | OtrlInsTag *p; 96 | char storeline[1000]; 97 | size_t maxsize = sizeof(storeline); 98 | 99 | while(fgets(storeline, maxsize, instf)) { 100 | char *prevpos; 101 | char *pos; 102 | unsigned int instag = 0; 103 | 104 | p = malloc(sizeof(*p)); 105 | if (!p) { 106 | return gcry_error(GPG_ERR_ENOMEM); 107 | } 108 | 109 | /* Parse the line, which should be of the form: 110 | * accountname\tprotocol\t40_hex_nybbles\n */ 111 | prevpos = storeline; 112 | pos = strchr(prevpos, '\t'); 113 | if (!pos) { 114 | free(p); 115 | continue; 116 | } 117 | *pos = '\0'; 118 | pos++; 119 | p->accountname = malloc(pos - prevpos); 120 | memmove(p->accountname, prevpos, pos - prevpos); 121 | 122 | prevpos = pos; 123 | pos = strchr(prevpos, '\t'); 124 | if (!pos) { 125 | free(p); 126 | continue; 127 | } 128 | *pos = '\0'; 129 | pos++; 130 | p->protocol = malloc(pos - prevpos); 131 | memmove(p->protocol, prevpos, pos - prevpos); 132 | 133 | prevpos = pos; 134 | pos = strchr(prevpos, '\r'); 135 | if (!pos) pos = strchr(prevpos, '\n'); 136 | if (!pos) { 137 | free(p); 138 | continue; 139 | } 140 | *pos = '\0'; 141 | pos++; 142 | /* hex str of length 8 */ 143 | if (strlen(prevpos) != 8) { 144 | free(p); 145 | continue; 146 | } 147 | 148 | sscanf(prevpos, "%08x", &instag); 149 | 150 | if (instag < OTRL_MIN_VALID_INSTAG) { 151 | free(p); 152 | continue; 153 | } 154 | p->instag = instag; 155 | 156 | /* Link it up */ 157 | p->next = us->instag_root; 158 | if (p->next) { 159 | p->next->tous = &(p->next); 160 | } 161 | p->tous = &(us->instag_root); 162 | us->instag_root = p; 163 | } 164 | 165 | return gcry_error(GPG_ERR_NO_ERROR); 166 | } 167 | 168 | /* Generate a new instance tag for the given account and write to file */ 169 | gcry_error_t otrl_instag_generate(OtrlUserState us, const char *filename, 170 | const char *accountname, const char *protocol) 171 | { 172 | gcry_error_t err; 173 | FILE *instf; 174 | 175 | /* Open the instance tag file. */ 176 | instf = fopen(filename, "wb"); 177 | if (!instf) { 178 | return gcry_error_from_errno(errno); 179 | } 180 | 181 | err = otrl_instag_generate_FILEp(us, instf, accountname, protocol); 182 | fclose(instf); 183 | return err; 184 | } 185 | 186 | /* Return a new valid instance tag */ 187 | otrl_instag_t otrl_instag_get_new() 188 | { 189 | otrl_instag_t result = 0; 190 | 191 | while(result < OTRL_MIN_VALID_INSTAG) { 192 | otrl_instag_t * instag = (otrl_instag_t *)gcry_random_bytes( 193 | sizeof(otrl_instag_t), GCRY_STRONG_RANDOM); 194 | result = *instag; 195 | gcry_free(instag); 196 | } 197 | 198 | return result; 199 | } 200 | 201 | /* Generate a new instance tag for the given account and write to file 202 | * The FILE* must be open for writing. */ 203 | gcry_error_t otrl_instag_generate_FILEp(OtrlUserState us, FILE *instf, 204 | const char *accountname, const char *protocol) 205 | { 206 | OtrlInsTag *p; 207 | if (!accountname || !protocol) return gcry_error(GPG_ERR_NO_ERROR); 208 | 209 | p = (OtrlInsTag *)malloc(sizeof(OtrlInsTag)); 210 | p->accountname = strdup(accountname); 211 | p->protocol = strdup(protocol); 212 | 213 | p->instag = otrl_instag_get_new(); 214 | 215 | /* Add to our list in OtrlUserState */ 216 | p->next = us->instag_root; 217 | if (p->next) { 218 | p->next->tous = &(p->next); 219 | } 220 | p->tous = &(us->instag_root); 221 | us->instag_root = p; 222 | 223 | otrl_instag_write_FILEp(us, instf); 224 | 225 | return gcry_error(GPG_ERR_NO_ERROR); 226 | } 227 | 228 | /* Write our instance tags to a file on disk. */ 229 | gcry_error_t otrl_instag_write(OtrlUserState us, const char *filename) 230 | { 231 | gcry_error_t err; 232 | FILE *instf; 233 | 234 | /* Open the instance tag file. */ 235 | instf = fopen(filename, "wb"); 236 | if (!instf) { 237 | return gcry_error_from_errno(errno); 238 | } 239 | 240 | err = otrl_instag_write_FILEp(us, instf); 241 | fclose(instf); 242 | return err; 243 | } 244 | 245 | /* Write our instance tags to a file on disk. 246 | * The FILE* must be open for writing. */ 247 | gcry_error_t otrl_instag_write_FILEp(OtrlUserState us, FILE *instf) 248 | { 249 | OtrlInsTag *p; 250 | /* This line should be ignored when read back in, since there are no 251 | tabs. */ 252 | fprintf(instf, "# WARNING! You shouldn't copy this file to another" 253 | " computer. It is unnecessary and can cause problems.\n"); 254 | for(p=us->instag_root; p; p=p->next) { 255 | fprintf(instf, "%s\t%s\t%08x\n", p->accountname, p->protocol, 256 | p->instag); 257 | } 258 | 259 | return gcry_error(GPG_ERR_NO_ERROR); 260 | } 261 | 262 | -------------------------------------------------------------------------------- /src/instag.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander, 4 | * Willy Lew, Lisa Du, Nikita Borisov 5 | * 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of version 2.1 of the GNU Lesser General 9 | * Public License as published by the Free Software Foundation. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #ifndef __INSTAG_H__ 22 | #define __INSTAG_H__ 23 | 24 | #include 25 | #include 26 | 27 | #define OTRL_INSTAG_MASTER 0 28 | #define OTRL_INSTAG_BEST 1 /* Most secure, based on: conv status, 29 | * then fingerprint status, then most recent. */ 30 | #define OTRL_INSTAG_RECENT 2 31 | #define OTRL_INSTAG_RECENT_RECEIVED 3 32 | #define OTRL_INSTAG_RECENT_SENT 4 33 | 34 | #define OTRL_MIN_VALID_INSTAG 0x100 /* Instag values below this are reserved 35 | * for meta instags, defined above, */ 36 | 37 | typedef unsigned int otrl_instag_t; 38 | 39 | /* The list of instance tags used for our accounts */ 40 | typedef struct s_OtrlInsTag { 41 | struct s_OtrlInsTag *next; 42 | struct s_OtrlInsTag **tous; 43 | 44 | char *accountname; 45 | char *protocol; 46 | otrl_instag_t instag; 47 | } OtrlInsTag; 48 | 49 | #include "userstate.h" 50 | 51 | /* Forget the given instag. */ 52 | void otrl_instag_forget(OtrlInsTag* instag); 53 | 54 | /* Forget all instags in a given OtrlUserState. */ 55 | void otrl_instag_forget_all(OtrlUserState us); 56 | 57 | /* Fetch the instance tag from the given OtrlUserState associated with 58 | * the given account */ 59 | OtrlInsTag * otrl_instag_find(OtrlUserState us, const char *accountname, 60 | const char *protocol); 61 | 62 | /* Read our instance tag from a file on disk into the given 63 | * OtrlUserState. */ 64 | gcry_error_t otrl_instag_read(OtrlUserState us, const char *filename); 65 | 66 | /* Read our instance tag from a file on disk into the given 67 | * OtrlUserState. The FILE* must be open for reading. */ 68 | gcry_error_t otrl_instag_read_FILEp(OtrlUserState us, FILE *instf); 69 | 70 | /* Return a new valid instance tag */ 71 | otrl_instag_t otrl_instag_get_new(); 72 | 73 | /* Get a new instance tag for the given account and write to file*/ 74 | gcry_error_t otrl_instag_generate(OtrlUserState us, const char *filename, 75 | const char *accountname, const char *protocol); 76 | 77 | /* Get a new instance tag for the given account and write to file 78 | * The FILE* must be open for writing. */ 79 | gcry_error_t otrl_instag_generate_FILEp(OtrlUserState us, FILE *instf, 80 | const char *accountname, const char *protocol); 81 | 82 | /* Write our instance tags to a file on disk. */ 83 | gcry_error_t otrl_instag_write(OtrlUserState us, const char *filename); 84 | 85 | /* Write our instance tags to a file on disk. 86 | * The FILE* must be open for writing. */ 87 | gcry_error_t otrl_instag_write_FILEp(OtrlUserState us, FILE *instf); 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /src/mem.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2014 Ian Goldberg, David Goulet, Rob Smits, 4 | * Chris Alexander, Willy Lew, Lisa Du, 5 | * Nikita Borisov 6 | * 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of version 2.1 of the GNU Lesser General 10 | * Public License as published by the Free Software Foundation. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public 18 | * License along with this library; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | 22 | /* Memory allocation routines for libgcrypt. All of the session key 23 | * information gets allocated through here, so we can wipe it out when 24 | * it's free()d. We don't use the built-in secmem functions of 25 | * libgcrypt because you need to declare a fixed amount of it when you 26 | * start up. 27 | * 28 | * Because "secure" and "insecure" allocations from libgcrypt will get 29 | * handled the same way (since we're not going to be running as root, 30 | * and so won't actually have pinned memory), pretend all allocated 31 | * memory (but just from libgcrypt) is requested secure, and wipe it on 32 | * free(). */ 33 | 34 | /* Uncomment the following to add a check that our free() and realloc() only 35 | * get called on things returned from our malloc(). */ 36 | /* #define OTRL_MEM_MAGIC 0x31415926 */ 37 | 38 | /* system headers */ 39 | #ifdef OTRL_MEM_MAGIC 40 | #include 41 | #endif 42 | #include 43 | 44 | /* libgcrypt headers */ 45 | #include 46 | 47 | /* libotr headers */ 48 | #include "mem.h" 49 | 50 | static size_t header_size; 51 | 52 | static void *otrl_mem_malloc(size_t n) 53 | { 54 | void *p; 55 | size_t new_n = n; 56 | new_n += header_size; 57 | 58 | /* Check for overflow attack */ 59 | if (new_n < n) return NULL; 60 | p = malloc(new_n); 61 | if (p == NULL) return NULL; 62 | 63 | ((size_t *)p)[0] = new_n; /* Includes header size */ 64 | #ifdef OTRL_MEM_MAGIC 65 | ((size_t *)p)[1] = OTRL_MEM_MAGIC; 66 | #endif 67 | 68 | return (void *)((char *)p + header_size); 69 | } 70 | 71 | static int otrl_mem_is_secure(const void *p) 72 | { 73 | return 1; 74 | } 75 | 76 | static void otrl_mem_free(void *p) 77 | { 78 | void *real_p = (void *)((char *)p - header_size); 79 | size_t n = ((size_t *)real_p)[0]; 80 | #ifdef OTRL_MEM_MAGIC 81 | if (((size_t *)real_p)[1] != OTRL_MEM_MAGIC) { 82 | fprintf(stderr, "Illegal free!\n"); 83 | return; 84 | } 85 | #endif 86 | 87 | /* Wipe the memory (in the same way the built-in deallocator in 88 | * libgcrypt would) */ 89 | memset(real_p, 0xff, n); 90 | memset(real_p, 0xaa, n); 91 | memset(real_p, 0x55, n); 92 | memset(real_p, 0x00, n); 93 | 94 | free(real_p); 95 | } 96 | 97 | static void *otrl_mem_realloc(void *p, size_t n) 98 | { 99 | if (p == NULL) { 100 | return otrl_mem_malloc(n); 101 | } else if (n == 0) { 102 | otrl_mem_free(p); 103 | return NULL; 104 | } else { 105 | void *real_p = (void *)((char *)p - header_size); 106 | void *new_p; 107 | size_t old_n = ((size_t *)real_p)[0]; 108 | #ifdef OTRL_MEM_MAGIC 109 | size_t magic = ((size_t *)real_p)[1]; 110 | #endif 111 | size_t new_n = n; 112 | new_n += header_size; 113 | 114 | /* Check for overflow attack */ 115 | if (new_n < n) return NULL; 116 | 117 | #ifdef OTRL_MEM_MAGIC 118 | if (magic != OTRL_MEM_MAGIC) { 119 | fprintf(stderr, "Illegal realloc!\n"); 120 | return NULL; 121 | } 122 | #endif 123 | 124 | if (new_n < old_n) { 125 | /* Overwrite the space we're about to stop using */ 126 | void *p = (void *)((char *)real_p + new_n); 127 | size_t excess = old_n - new_n; 128 | memset(p, 0xff, excess); 129 | memset(p, 0xaa, excess); 130 | memset(p, 0x55, excess); 131 | memset(p, 0x00, excess); 132 | 133 | /* We don't actually need to realloc() */ 134 | new_p = real_p; 135 | } else { 136 | new_p = realloc(real_p, new_n); 137 | if (new_p == NULL) return NULL; 138 | } 139 | 140 | ((size_t *)new_p)[0] = new_n; /* Includes header size */ 141 | return (void *)((char *)new_p + header_size); 142 | } 143 | } 144 | 145 | void otrl_mem_init(void) 146 | { 147 | header_size = 8; 148 | #ifdef OTRL_MEM_MAGIC 149 | if (header_size < 2*sizeof(size_t)) { 150 | header_size = 2*sizeof(size_t); 151 | } 152 | #else 153 | if (header_size < sizeof(size_t)) { 154 | header_size = sizeof(size_t); 155 | } 156 | #endif 157 | 158 | gcry_set_allocation_handler( 159 | otrl_mem_malloc, 160 | otrl_mem_malloc, 161 | otrl_mem_is_secure, 162 | otrl_mem_realloc, 163 | otrl_mem_free 164 | ); 165 | } 166 | 167 | /* Compare two memory blocks in time dependent on the length of the 168 | * blocks, but not their contents. Returns 1 if they differ, 0 if they 169 | * are the same. */ 170 | int otrl_mem_differ(const unsigned char *buf1, const unsigned char *buf2, 171 | size_t len) 172 | { 173 | volatile unsigned char diff = 0; 174 | size_t i; 175 | 176 | for (i = 0; i < len; ++i) { 177 | diff |= (buf1[i] ^ buf2[i]); 178 | } 179 | return (diff != 0); 180 | } 181 | -------------------------------------------------------------------------------- /src/mem.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2014 Ian Goldberg, David Goulet, Rob Smits, 4 | * Chris Alexander, Willy Lew, Lisa Du, 5 | * Nikita Borisov 6 | * 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of version 2.1 of the GNU Lesser General 10 | * Public License as published by the Free Software Foundation. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public 18 | * License along with this library; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | 22 | #ifndef __MEM_H__ 23 | #define __MEM_H__ 24 | 25 | #include 26 | 27 | void otrl_mem_init(void); 28 | 29 | /* Compare two memory blocks in time dependent on the length of the 30 | * blocks, but not their contents. Returns 1 if they differ, 0 if they 31 | * are the same. */ 32 | int otrl_mem_differ(const unsigned char *buf1, const unsigned char *buf2, 33 | size_t len); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/privkey-t.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew, 4 | * Nikita Borisov 5 | * 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of version 2.1 of the GNU Lesser General 9 | * Public License as published by the Free Software Foundation. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #ifndef __PRIVKEY_T_H__ 22 | #define __PRIVKEY_T_H__ 23 | 24 | #include 25 | 26 | typedef struct s_OtrlPrivKey { 27 | struct s_OtrlPrivKey *next; 28 | struct s_OtrlPrivKey **tous; 29 | 30 | char *accountname; 31 | char *protocol; 32 | unsigned short pubkey_type; 33 | gcry_sexp_t privkey; 34 | unsigned char *pubkey_data; 35 | size_t pubkey_datalen; 36 | } OtrlPrivKey; 37 | 38 | #define OTRL_PUBKEY_TYPE_DSA 0x0000 39 | 40 | /* The list of privkeys currently being constructed, possibly in a 41 | * background thread */ 42 | typedef struct s_OtrlPendingPrivKey { 43 | struct s_OtrlPendingPrivKey *next; 44 | struct s_OtrlPendingPrivKey **tous; 45 | 46 | char *accountname; 47 | char *protocol; 48 | } OtrlPendingPrivKey; 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /src/privkey.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew, 4 | * Lisa Du, Nikita Borisov 5 | * 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of version 2.1 of the GNU Lesser General 9 | * Public License as published by the Free Software Foundation. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #ifndef __PRIVKEY_H__ 22 | #define __PRIVKEY_H__ 23 | 24 | #include 25 | #include "privkey-t.h" 26 | #include "userstate.h" 27 | 28 | /* The length of a string representing a human-readable version of a 29 | * fingerprint (including the trailing NUL) */ 30 | #define OTRL_PRIVKEY_FPRINT_HUMAN_LEN 45 31 | 32 | /* Convert a 20-byte hash value to a 45-byte human-readable value */ 33 | void otrl_privkey_hash_to_human( 34 | char human[OTRL_PRIVKEY_FPRINT_HUMAN_LEN], 35 | const unsigned char hash[20]); 36 | 37 | /* Calculate a human-readable hash of our DSA public key. Return it in 38 | * the passed fingerprint buffer. Return NULL on error, or a pointer to 39 | * the given buffer on success. */ 40 | char *otrl_privkey_fingerprint(OtrlUserState us, 41 | char fingerprint[OTRL_PRIVKEY_FPRINT_HUMAN_LEN], 42 | const char *accountname, const char *protocol); 43 | 44 | /* Calculate a raw hash of our DSA public key. Return it in the passed 45 | * fingerprint buffer. Return NULL on error, or a pointer to the given 46 | * buffer on success. */ 47 | unsigned char *otrl_privkey_fingerprint_raw(OtrlUserState us, 48 | unsigned char hash[20], const char *accountname, const char *protocol); 49 | 50 | /* Read a sets of private DSA keys from a file on disk into the given 51 | * OtrlUserState. */ 52 | gcry_error_t otrl_privkey_read(OtrlUserState us, const char *filename); 53 | 54 | /* Read a sets of private DSA keys from a FILE* into the given 55 | * OtrlUserState. The FILE* must be open for reading. */ 56 | gcry_error_t otrl_privkey_read_FILEp(OtrlUserState us, FILE *privf); 57 | 58 | /* Free the memory associated with the pending privkey list */ 59 | void otrl_privkey_pending_forget_all(OtrlUserState us); 60 | 61 | /* Begin a private key generation that will potentially take place in 62 | * a background thread. This routine must be called from the main 63 | * thread. It will set *newkeyp, which you can pass to 64 | * otrl_privkey_generate_calculate in a background thread. If it 65 | * returns gcry_error(GPG_ERR_EEXIST), then a privkey creation for 66 | * this accountname/protocol is already in progress, and *newkeyp will 67 | * be set to NULL. */ 68 | gcry_error_t otrl_privkey_generate_start(OtrlUserState us, 69 | const char *accountname, const char *protocol, void **newkeyp); 70 | 71 | /* Do the private key generation calculation. You may call this from a 72 | * background thread. When it completes, call 73 | * otrl_privkey_generate_finish from the _main_ thread. */ 74 | gcry_error_t otrl_privkey_generate_calculate(void *newkey); 75 | 76 | /* Call this from the main thread only. It will write the newly created 77 | * private key into the given file and store it in the OtrlUserState. */ 78 | gcry_error_t otrl_privkey_generate_finish(OtrlUserState us, 79 | void *newkey, const char *filename); 80 | 81 | /* Call this from the main thread only. It will write the newly created 82 | * private key into the given FILE* (which must be open for reading and 83 | * writing) and store it in the OtrlUserState. */ 84 | gcry_error_t otrl_privkey_generate_finish_FILEp(OtrlUserState us, 85 | void *newkey, FILE *privf); 86 | 87 | /* Call this from the main thread only, in the event that the background 88 | * thread generating the key is cancelled. The newkey is deallocated, 89 | * and must not be used further. */ 90 | void otrl_privkey_generate_cancelled(OtrlUserState us, void *newkey); 91 | 92 | /* Generate a private DSA key for a given account, storing it into a 93 | * file on disk, and loading it into the given OtrlUserState. Overwrite any 94 | * previously generated keys for that account in that OtrlUserState. */ 95 | gcry_error_t otrl_privkey_generate(OtrlUserState us, const char *filename, 96 | const char *accountname, const char *protocol); 97 | 98 | /* Generate a private DSA key for a given account, storing it into a 99 | * FILE*, and loading it into the given OtrlUserState. Overwrite any 100 | * previously generated keys for that account in that OtrlUserState. 101 | * The FILE* must be open for reading and writing. */ 102 | gcry_error_t otrl_privkey_generate_FILEp(OtrlUserState us, FILE *privf, 103 | const char *accountname, const char *protocol); 104 | 105 | /* Read the fingerprint store from a file on disk into the given 106 | * OtrlUserState. Use add_app_data to add application data to each 107 | * ConnContext so created. */ 108 | gcry_error_t otrl_privkey_read_fingerprints(OtrlUserState us, 109 | const char *filename, 110 | void (*add_app_data)(void *data, ConnContext *context), 111 | void *data); 112 | 113 | /* Read the fingerprint store from a FILE* into the given 114 | * OtrlUserState. Use add_app_data to add application data to each 115 | * ConnContext so created. The FILE* must be open for reading. */ 116 | gcry_error_t otrl_privkey_read_fingerprints_FILEp(OtrlUserState us, 117 | FILE *storef, 118 | void (*add_app_data)(void *data, ConnContext *context), 119 | void *data); 120 | 121 | /* Write the fingerprint store from a given OtrlUserState to a file on disk. */ 122 | gcry_error_t otrl_privkey_write_fingerprints(OtrlUserState us, 123 | const char *filename); 124 | 125 | /* Write the fingerprint store from a given OtrlUserState to a FILE*. 126 | * The FILE* must be open for writing. */ 127 | gcry_error_t otrl_privkey_write_fingerprints_FILEp(OtrlUserState us, 128 | FILE *storef); 129 | 130 | /* Fetch the private key from the given OtrlUserState associated with 131 | * the given account */ 132 | OtrlPrivKey *otrl_privkey_find(OtrlUserState us, const char *accountname, 133 | const char *protocol); 134 | 135 | /* Forget a private key */ 136 | void otrl_privkey_forget(OtrlPrivKey *privkey); 137 | 138 | /* Forget all private keys in a given OtrlUserState. */ 139 | void otrl_privkey_forget_all(OtrlUserState us); 140 | 141 | /* Sign data using a private key. The data must be small enough to be 142 | * signed (i.e. already hashed, if necessary). The signature will be 143 | * returned in *sigp, which the caller must free(). Its length will be 144 | * returned in *siglenp. */ 145 | gcry_error_t otrl_privkey_sign(unsigned char **sigp, size_t *siglenp, 146 | OtrlPrivKey *privkey, const unsigned char *data, size_t len); 147 | 148 | /* Verify a signature on data using a public key. The data must be 149 | * small enough to be signed (i.e. already hashed, if necessary). */ 150 | gcry_error_t otrl_privkey_verify(const unsigned char *sigbuf, size_t siglen, 151 | unsigned short pubkey_type, gcry_sexp_t pubs, 152 | const unsigned char *data, size_t len); 153 | 154 | #endif 155 | -------------------------------------------------------------------------------- /src/proto.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander, 4 | * Willy Lew, Lisa Du, Nikita Borisov 5 | * 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of version 2.1 of the GNU Lesser General 9 | * Public License as published by the Free Software Foundation. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #ifndef __PROTO_H__ 22 | #define __PROTO_H__ 23 | 24 | #include "context.h" 25 | #include "version.h" 26 | #include "tlv.h" 27 | 28 | /* If we ever see this sequence in a plaintext message, we'll assume the 29 | * other side speaks OTR, and try to establish a connection. */ 30 | #define OTRL_MESSAGE_TAG_BASE " \t \t\t\t\t \t \t \t " 31 | /* The following must each be of length 8 */ 32 | #define OTRL_MESSAGE_TAG_V1 " \t \t \t " 33 | #define OTRL_MESSAGE_TAG_V2 " \t\t \t " 34 | #define OTRL_MESSAGE_TAG_V3 " \t\t \t\t" 35 | 36 | /* The possible flags contained in a Data Message */ 37 | #define OTRL_MSGFLAGS_IGNORE_UNREADABLE 0x01 38 | 39 | typedef unsigned int OtrlPolicy; 40 | 41 | #define OTRL_POLICY_ALLOW_V1 0x01 42 | #define OTRL_POLICY_ALLOW_V2 0x02 43 | #define OTRL_POLICY_ALLOW_V3 0x04 44 | #define OTRL_POLICY_REQUIRE_ENCRYPTION 0x08 45 | #define OTRL_POLICY_SEND_WHITESPACE_TAG 0x10 46 | #define OTRL_POLICY_WHITESPACE_START_AKE 0x20 47 | #define OTRL_POLICY_ERROR_START_AKE 0x40 48 | 49 | #define OTRL_POLICY_VERSION_MASK (OTRL_POLICY_ALLOW_V1 | OTRL_POLICY_ALLOW_V2 |\ 50 | OTRL_POLICY_ALLOW_V3) 51 | 52 | /* Length of OTR message headers */ 53 | #define OTRL_HEADER_LEN 3 54 | #define OTRL_B64_HEADER_LEN 4 55 | 56 | /* Analogous to v1 policies */ 57 | #define OTRL_POLICY_NEVER 0x00 58 | #define OTRL_POLICY_OPPORTUNISTIC \ 59 | ( OTRL_POLICY_ALLOW_V2 | \ 60 | OTRL_POLICY_ALLOW_V3 | \ 61 | OTRL_POLICY_SEND_WHITESPACE_TAG | \ 62 | OTRL_POLICY_WHITESPACE_START_AKE | \ 63 | OTRL_POLICY_ERROR_START_AKE ) 64 | #define OTRL_POLICY_MANUAL \ 65 | ( OTRL_POLICY_ALLOW_V2 | \ 66 | OTRL_POLICY_ALLOW_V3) 67 | #define OTRL_POLICY_ALWAYS \ 68 | ( OTRL_POLICY_ALLOW_V2 | \ 69 | OTRL_POLICY_ALLOW_V3 | \ 70 | OTRL_POLICY_REQUIRE_ENCRYPTION | \ 71 | OTRL_POLICY_WHITESPACE_START_AKE | \ 72 | OTRL_POLICY_ERROR_START_AKE ) 73 | #define OTRL_POLICY_DEFAULT OTRL_POLICY_OPPORTUNISTIC 74 | 75 | typedef enum { 76 | OTRL_MSGTYPE_NOTOTR, 77 | OTRL_MSGTYPE_TAGGEDPLAINTEXT, 78 | OTRL_MSGTYPE_QUERY, 79 | OTRL_MSGTYPE_DH_COMMIT, 80 | OTRL_MSGTYPE_DH_KEY, 81 | OTRL_MSGTYPE_REVEALSIG, 82 | OTRL_MSGTYPE_SIGNATURE, 83 | OTRL_MSGTYPE_V1_KEYEXCH, 84 | OTRL_MSGTYPE_DATA, 85 | OTRL_MSGTYPE_ERROR, 86 | OTRL_MSGTYPE_UNKNOWN 87 | } OtrlMessageType; 88 | 89 | typedef enum { 90 | OTRL_FRAGMENT_UNFRAGMENTED, 91 | OTRL_FRAGMENT_INCOMPLETE, 92 | OTRL_FRAGMENT_COMPLETE 93 | } OtrlFragmentResult; 94 | 95 | typedef enum { 96 | OTRL_FRAGMENT_SEND_SKIP, /* Return new message back to caller, 97 | * but don't inject. */ 98 | OTRL_FRAGMENT_SEND_ALL, 99 | OTRL_FRAGMENT_SEND_ALL_BUT_FIRST, 100 | OTRL_FRAGMENT_SEND_ALL_BUT_LAST 101 | } OtrlFragmentPolicy; 102 | 103 | /* Initialize the OTR library. Pass the version of the API you are 104 | * using. */ 105 | gcry_error_t otrl_init(unsigned int ver_major, unsigned int ver_minor, 106 | unsigned int ver_sub); 107 | 108 | /* Shortcut */ 109 | #define OTRL_INIT do { \ 110 | if (otrl_init(OTRL_VERSION_MAJOR, OTRL_VERSION_MINOR, \ 111 | OTRL_VERSION_SUB)) { \ 112 | exit(1); \ 113 | } \ 114 | } while(0) 115 | 116 | /* Return a pointer to a static string containing the version number of 117 | * the OTR library. */ 118 | const char *otrl_version(void); 119 | 120 | /* Return a pointer to a newly-allocated OTR query message, customized 121 | * with our name. The caller should free() the result when he's done 122 | * with it. */ 123 | char *otrl_proto_default_query_msg(const char *ourname, OtrlPolicy policy); 124 | 125 | /* Return the best version of OTR support by both sides, given an OTR 126 | * Query Message and the local policy. */ 127 | unsigned int otrl_proto_query_bestversion(const char *querymsg, 128 | OtrlPolicy policy); 129 | 130 | /* Locate any whitespace tag in this message, and return the best 131 | * version of OTR support on both sides. Set *starttagp and *endtagp to 132 | * the start and end of the located tag, so that it can be snipped out. */ 133 | unsigned int otrl_proto_whitespace_bestversion(const char *msg, 134 | const char **starttagp, const char **endtagp, OtrlPolicy policy); 135 | 136 | /* Find the message type. */ 137 | OtrlMessageType otrl_proto_message_type(const char *message); 138 | 139 | /* Find the message version. */ 140 | int otrl_proto_message_version(const char *message); 141 | 142 | /* Find the instance tags in this message. */ 143 | gcry_error_t otrl_proto_instance(const char *otrmsg, 144 | unsigned int *instance_from, unsigned int *instance_to); 145 | 146 | /* Create an OTR Data message. Pass the plaintext as msg, and an 147 | * optional chain of TLVs. A newly-allocated string will be returned in 148 | * *encmessagep. Put the current extra symmetric key into extrakey 149 | * (if non-NULL). */ 150 | gcry_error_t otrl_proto_create_data(char **encmessagep, ConnContext *context, 151 | const char *msg, const OtrlTLV *tlvs, unsigned char flags, 152 | unsigned char *extrakey); 153 | 154 | /* Extract the flags from an otherwise unreadable Data Message. */ 155 | gcry_error_t otrl_proto_data_read_flags(const char *datamsg, 156 | unsigned char *flagsp); 157 | 158 | /* Accept an OTR Data Message in datamsg. Decrypt it and put the 159 | * plaintext into *plaintextp, and any TLVs into tlvsp. Put any 160 | * received flags into *flagsp (if non-NULL). Put the current extra 161 | * symmetric key into extrakey (if non-NULL). */ 162 | gcry_error_t otrl_proto_accept_data(char **plaintextp, OtrlTLV **tlvsp, 163 | ConnContext *context, const char *datamsg, unsigned char *flagsp, 164 | unsigned char *extrakey); 165 | 166 | /* Accumulate a potential fragment into the current context. */ 167 | OtrlFragmentResult otrl_proto_fragment_accumulate(char **unfragmessagep, 168 | ConnContext *context, const char *msg); 169 | 170 | gcry_error_t otrl_proto_fragment_create(int mms, int fragment_count, 171 | char ***fragments, ConnContext *context, const char *message); 172 | 173 | void otrl_proto_fragment_free(char ***fragments, unsigned short arraylen); 174 | #endif 175 | -------------------------------------------------------------------------------- /src/serial.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander, 4 | * Willy Lew, Lisa Du, Nikita Borisov 5 | * 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of version 2.1 of the GNU Lesser General 9 | * Public License as published by the Free Software Foundation. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #ifndef __SERIAL_H__ 22 | #define __SERIAL_H__ 23 | 24 | #undef DEBUG 25 | 26 | #ifdef DEBUG 27 | 28 | #include 29 | 30 | #define debug_data(t,b,l) do { const unsigned char *data = (b); size_t i; \ 31 | fprintf(stderr, "%s: ", (t)); \ 32 | for(i=0;i<(l);++i) { \ 33 | fprintf(stderr, "%02x", data[i]); \ 34 | } \ 35 | fprintf(stderr, "\n"); \ 36 | } while(0) 37 | 38 | #define debug_int(t,b) do { const unsigned char *data = (b); \ 39 | unsigned int v = \ 40 | (((unsigned int)data[0]) << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; \ 41 | fprintf(stderr, "%s: %u (0x%x)\n", (t), v, v); \ 42 | } while(0) 43 | 44 | #else 45 | #define debug_data(t,b,l) 46 | #define debug_int(t,b) 47 | #endif 48 | 49 | #define write_int(x) do { \ 50 | bufp[0] = ((x) >> 24) & 0xff; \ 51 | bufp[1] = ((x) >> 16) & 0xff; \ 52 | bufp[2] = ((x) >> 8) & 0xff; \ 53 | bufp[3] = (x) & 0xff; \ 54 | bufp += 4; lenp -= 4; \ 55 | } while(0) 56 | 57 | #define write_mpi(x,nx,dx) do { \ 58 | write_int(nx); \ 59 | gcry_mpi_print(format, bufp, lenp, NULL, (x)); \ 60 | debug_data((dx), bufp, (nx)); \ 61 | bufp += (nx); lenp -= (nx); \ 62 | } while(0) 63 | 64 | #define require_len(l) do { \ 65 | if (lenp < (l)) goto invval; \ 66 | } while(0) 67 | 68 | #define read_int(x) do { \ 69 | require_len(4); \ 70 | (x) = (((unsigned int)bufp[0]) << 24) | (bufp[1] << 16) | (bufp[2] << 8) | bufp[3]; \ 71 | bufp += 4; lenp -= 4; \ 72 | } while(0) 73 | 74 | #define read_mpi(x) do { \ 75 | size_t mpilen; \ 76 | read_int(mpilen); \ 77 | if (mpilen) { \ 78 | require_len(mpilen); \ 79 | gcry_mpi_scan(&(x), GCRYMPI_FMT_USG, bufp, mpilen, NULL); \ 80 | } else { \ 81 | (x) = gcry_mpi_set_ui(NULL, 0); \ 82 | } \ 83 | bufp += mpilen; lenp -= mpilen; \ 84 | } while(0) 85 | 86 | /* Write version and msg type into bufp*/ 87 | #define write_header(version, msgtype) do { \ 88 | bufp[0] = 0x00; \ 89 | bufp[1] = version & 0xff; \ 90 | bufp[2] = msgtype; \ 91 | debug_data("Header", bufp, 3); \ 92 | bufp += 3; lenp -= 3; \ 93 | } while(0) 94 | 95 | /* Verify msg header is v1, v2 or v3 and has type x, 96 | * increment bufp past msg header */ 97 | #define skip_header(x) do { \ 98 | require_len(3); \ 99 | if ((bufp[0] != 0x00) || (bufp[2] != x)) \ 100 | goto invval; \ 101 | if ((bufp[1] == 0x01) || (bufp[1] == 0x02) || \ 102 | (bufp[1] == 0x03)) { \ 103 | bufp += 3; lenp -= 3; \ 104 | } else goto invval; \ 105 | } while(0) 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /src/sm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew, 4 | * Nikita Borisov 5 | * 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of version 2.1 of the GNU Lesser General 9 | * Public License as published by the Free Software Foundation. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #ifndef __SM_H__ 22 | #define __SM_H__ 23 | 24 | #include 25 | 26 | #define SM_HASH_ALGORITHM GCRY_MD_SHA256 27 | #define SM_DIGEST_SIZE 32 28 | 29 | typedef enum { 30 | OTRL_SMP_EXPECT1, 31 | OTRL_SMP_EXPECT2, 32 | OTRL_SMP_EXPECT3, 33 | OTRL_SMP_EXPECT4, 34 | OTRL_SMP_EXPECT5 35 | } NextExpectedSMP; 36 | 37 | typedef enum { 38 | OTRL_SMP_PROG_OK = 0, /* All is going fine so far */ 39 | OTRL_SMP_PROG_CHEATED = -2, /* Some verification failed */ 40 | OTRL_SMP_PROG_FAILED = -1, /* The secrets didn't match */ 41 | OTRL_SMP_PROG_SUCCEEDED = 1 /* The SMP completed successfully */ 42 | } OtrlSMProgState; 43 | 44 | typedef struct { 45 | gcry_mpi_t secret, x2, x3, g1, g2, g3, g3o, p, q, pab, qab; 46 | NextExpectedSMP nextExpected; 47 | int received_question; /* 1 if we received a question in an SMP1Q TLV */ 48 | OtrlSMProgState sm_prog_state; 49 | } OtrlSMState; 50 | 51 | typedef OtrlSMState OtrlSMAliceState; 52 | typedef OtrlSMState OtrlSMBobState; 53 | 54 | /* 55 | * Call this once, at plugin load time. It sets up the modulus and 56 | * generator MPIs. 57 | */ 58 | void otrl_sm_init(void); 59 | 60 | /* 61 | * Initialize the fields of a SM state. 62 | */ 63 | void otrl_sm_state_new(OtrlSMState *smst); 64 | 65 | /* 66 | * Initialize the fields of a SM state. Called the first time that 67 | * a user begins an SMP session. 68 | */ 69 | void otrl_sm_state_init(OtrlSMState *smst); 70 | 71 | /* 72 | * Deallocate the contents of a OtrlSMState (but not the OtrlSMState 73 | * itself) 74 | */ 75 | void otrl_sm_state_free(OtrlSMState *smst); 76 | 77 | gcry_error_t otrl_sm_step1(OtrlSMAliceState *astate, const unsigned char* secret, int secretlen, unsigned char** output, int* outputlen); 78 | gcry_error_t otrl_sm_step2a(OtrlSMBobState *bstate, const unsigned char* input, const int inputlen, int received_question); 79 | gcry_error_t otrl_sm_step2b(OtrlSMBobState *bstate, const unsigned char* secret, int secretlen, unsigned char **output, int* outputlen); 80 | gcry_error_t otrl_sm_step3(OtrlSMAliceState *astate, const unsigned char* input, const int inputlen, unsigned char **output, int* outputlen); 81 | gcry_error_t otrl_sm_step4(OtrlSMBobState *bstate, const unsigned char* input, const int inputlen, unsigned char **output, int* outputlen); 82 | gcry_error_t otrl_sm_step5(OtrlSMAliceState *astate, const unsigned char* input, const int inputlen); 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /src/tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "proto.h" 5 | #include "privkey.h" 6 | #include "message.h" 7 | #include "instag.h" 8 | 9 | #define ALICE "alice" 10 | #define BOB "bob" 11 | #define PROTO "prpl-oscar" 12 | 13 | static OtrlPolicy ALICEPOLICY = OTRL_POLICY_DEFAULT &~ OTRL_POLICY_ALLOW_V1; 14 | static OtrlPolicy BOBPOLICY = OTRL_POLICY_DEFAULT; 15 | void receiving(const char *from, const char *to, const char *msg); 16 | 17 | typedef struct s_node { 18 | struct s_node *next; 19 | char *from, *to, *msg; 20 | } MsgNode; 21 | 22 | static MsgNode *noderoot = NULL; 23 | static MsgNode **nodeend = &noderoot; 24 | 25 | void otrl_sm_init(void) {} 26 | void otrl_sm_state_new(OtrlSMState *smst) {} 27 | void otrl_sm_state_init(OtrlSMState *smst) {} 28 | void otrl_sm_state_free(OtrlSMState *smst) {} 29 | gcry_error_t otrl_sm_step1(OtrlSMAliceState *astate, const unsigned char* secret, int secretlen, unsigned char** output, int* outputlen) {return gcry_error(GPG_ERR_NO_ERROR);} 30 | gcry_error_t otrl_sm_step2a(OtrlSMBobState *bstate, const unsigned char* input, const int inputlen, int received_question) {return gcry_error(GPG_ERR_NO_ERROR);} 31 | gcry_error_t otrl_sm_step2b(OtrlSMBobState *bstate, const unsigned char* secret, int secretlen, unsigned char **output, int* outputlen) {return gcry_error(GPG_ERR_NO_ERROR);} 32 | gcry_error_t otrl_sm_step3(OtrlSMAliceState *astate, const unsigned char* input, const int inputlen, unsigned char **output, int* outputlen) {return gcry_error(GPG_ERR_NO_ERROR);} 33 | gcry_error_t otrl_sm_step4(OtrlSMBobState *bstate, const unsigned char* input, const int inputlen, unsigned char **output, int* outputlen) {return gcry_error(GPG_ERR_NO_ERROR);} 34 | gcry_error_t otrl_sm_step5(OtrlSMAliceState *astate, const unsigned char* input, const int inputlen) {return gcry_error(GPG_ERR_NO_ERROR);} 35 | 36 | 37 | static void dispatch(void) 38 | { 39 | while(noderoot) { 40 | MsgNode *node = noderoot; 41 | 42 | receiving(node->from, node->to, node->msg); 43 | free(node->from); 44 | free(node->to); 45 | free(node->msg); 46 | noderoot = node->next; 47 | free(node); 48 | if (noderoot == NULL) nodeend = &noderoot; 49 | } 50 | } 51 | 52 | static void inject(const char *from, const char *to, const char *msg) 53 | { 54 | MsgNode *node = malloc(sizeof(*node)); 55 | node->from = strdup(from); 56 | node->to = strdup(to); 57 | node->msg = strdup(msg); 58 | node->next = NULL; 59 | *nodeend = node; 60 | nodeend = &(node->next); 61 | printf("[%s->%s: %s]\n\n", from, to, msg); 62 | } 63 | 64 | static OtrlPolicy op_policy(void *opdata, ConnContext *context) 65 | { 66 | if (!strcmp(context->accountname, ALICE)) return ALICEPOLICY; 67 | if (!strcmp(context->accountname, BOB)) return BOBPOLICY; 68 | return OTRL_POLICY_DEFAULT; 69 | } 70 | 71 | static void op_inject(void *opdata, const char *accountname, 72 | const char *protocol, const char *recipient, const char *message) 73 | { 74 | inject(accountname, recipient, message); 75 | } 76 | 77 | static void op_notify(void *opdata, OtrlNotifyLevel level, 78 | const char *accountname, const char *protocol, 79 | const char *username, const char *title, 80 | const char *primary, const char *secondary) 81 | { 82 | } 83 | 84 | static int op_display_otr_message(void *opdata, const char *accountname, 85 | const char *protocol, const char *username, const char *msg) 86 | { 87 | return -1; 88 | } 89 | 90 | static void op_gone_secure(void *opdata, ConnContext *context) 91 | { 92 | printf("SECURE (%d): %s / %s\n\n", context->protocol_version, 93 | context->accountname, context->username); 94 | } 95 | 96 | static void op_gone_insecure(void *opdata, ConnContext *context) 97 | { 98 | printf("INSECURE: %s / %s\n\n", context->accountname, context->username); 99 | } 100 | 101 | static void op_still_secure(void *opdata, ConnContext *context, int is_reply) 102 | { 103 | printf("REFRESH (%d/%d): %s / %s\n\n", is_reply, context->protocol_version, 104 | context->accountname, context->username); 105 | } 106 | 107 | static OtrlMessageAppOps ops = { 108 | op_policy, 109 | NULL, 110 | NULL, 111 | op_inject, 112 | op_notify, 113 | op_display_otr_message, 114 | NULL, 115 | NULL, 116 | NULL, 117 | NULL, 118 | NULL, 119 | op_gone_secure, 120 | op_gone_insecure, 121 | op_still_secure, 122 | NULL 123 | }; 124 | 125 | static OtrlUserState us; 126 | 127 | void receiving(const char *from, const char *to, const char *msg) 128 | { 129 | int ignore; 130 | char *newmsg; 131 | OtrlTLV *tlvs; 132 | 133 | ignore = otrl_message_receiving(us, &ops, NULL, to, PROTO, from, msg, 134 | &newmsg, &tlvs, NULL, NULL); 135 | 136 | if (!ignore) { 137 | printf("%s> %s\n\n", from, newmsg ? newmsg : msg); 138 | } 139 | 140 | otrl_message_free(newmsg); 141 | otrl_tlv_free(tlvs); 142 | } 143 | 144 | static void sending(const char *from, const char *to, const char *msg) 145 | { 146 | gcry_error_t err; 147 | OtrlTLV *tlvs = NULL; 148 | char *newmsg; 149 | 150 | err = otrl_message_sending(us, &ops, NULL, from, PROTO, to, msg, 151 | tlvs, &newmsg, NULL, NULL, NULL); 152 | 153 | if (!err) { 154 | inject(from, to, newmsg ? newmsg : msg); 155 | } 156 | 157 | otrl_message_free(newmsg); 158 | otrl_tlv_free(tlvs); 159 | } 160 | 161 | static void test(int vers, int both) 162 | { 163 | printf("\n\n*** Testing version %d, %s ***\n\n", vers, 164 | both ? "simultaneous start" : "Alice start"); 165 | 166 | otrl_context_forget_all(us); 167 | if (vers == 1) 168 | ALICEPOLICY = OTRL_POLICY_ALLOW_V1; 169 | else if (vers == 2) 170 | ALICEPOLICY = OTRL_POLICY_ALLOW_V2; 171 | else 172 | ALICEPOLICY = OTRL_POLICY_DEFAULT; 173 | sending(ALICE, BOB, "?OTR?"); 174 | if (both) { 175 | sending(BOB, ALICE, "?OTR?"); 176 | } 177 | dispatch(); 178 | sending(ALICE, BOB, "Hi there"); 179 | dispatch(); 180 | } 181 | 182 | void test_unreadable(void) 183 | { 184 | ConnContext *bobcontext; 185 | 186 | printf("\n\n*** Testing Bob receiving unreadable messages from " 187 | "Alice ***\n\n"); 188 | 189 | bobcontext = otrl_context_find(us, ALICE, BOB, PROTO, 0, 0, NULL, NULL, NULL); 190 | otrl_context_force_plaintext(bobcontext); 191 | sending(ALICE, BOB, "unreadable text"); 192 | dispatch(); 193 | 194 | } 195 | 196 | void test_crash1(void) 197 | { 198 | ConnContext *alicecontext, *bobcontext; 199 | 200 | printf("\n\n*** Testing old double gcry_cipher_release case ***\n\n"); 201 | 202 | otrl_context_forget_all(us); 203 | ALICEPOLICY = OTRL_POLICY_DEFAULT; 204 | sending(ALICE, BOB, "?OTR?"); 205 | dispatch(); 206 | 207 | alicecontext = otrl_context_find(us, BOB, ALICE, PROTO, 0, 0, NULL, NULL, NULL); 208 | bobcontext = otrl_context_find(us, ALICE, BOB, PROTO, 0, 0, NULL, NULL, NULL); 209 | 210 | sending(ALICE, BOB, "Hi!"); dispatch(); 211 | sending(BOB, ALICE, "There!"); dispatch(); 212 | sending(ALICE, BOB, "You!"); dispatch(); 213 | otrl_context_force_plaintext(bobcontext); 214 | sending(BOB, ALICE, "?OTR?"); dispatch(); 215 | sending(ALICE, BOB, "now."); dispatch(); 216 | printf("%d %p %p\n", alicecontext->our_keyid, alicecontext->their_y, alicecontext->their_old_y); 217 | printf("%p %p %p %p\n", 218 | alicecontext->sesskeys[0][0].sendenc, 219 | alicecontext->sesskeys[0][1].sendenc, 220 | alicecontext->sesskeys[1][0].sendenc, 221 | alicecontext->sesskeys[1][1].sendenc); 222 | sending(BOB, ALICE, "then."); dispatch(); 223 | } 224 | 225 | void test_refresh(int vers) 226 | { 227 | ConnContext *alicecontext, *bobcontext; 228 | 229 | printf("\n\n*** Testing refresh ***\n\n"); 230 | 231 | otrl_context_forget_all(us); 232 | if (vers == 1) 233 | ALICEPOLICY = OTRL_POLICY_ALLOW_V1; 234 | else if (vers == 2) 235 | ALICEPOLICY = OTRL_POLICY_ALLOW_V2; 236 | else 237 | ALICEPOLICY = OTRL_POLICY_DEFAULT; 238 | sending(ALICE, BOB, "?OTR?"); dispatch(); 239 | 240 | alicecontext = otrl_context_find(us, BOB, ALICE, PROTO, 0, 0, NULL, NULL, NULL); 241 | bobcontext = otrl_context_find(us, ALICE, BOB, PROTO, 0, 0, NULL, NULL, NULL); 242 | printf("%p %p\n", alicecontext, bobcontext); 243 | 244 | sending(ALICE, BOB, "Hi!"); dispatch(); 245 | sending(BOB, ALICE, "There!"); dispatch(); 246 | sending(ALICE, BOB, "You!"); dispatch(); 247 | sending(ALICE, BOB, "Guys!"); dispatch(); 248 | sending(BOB, ALICE, "?OTR?"); dispatch(); 249 | sending(ALICE, BOB, "Refreshed!"); dispatch(); 250 | sending(BOB, ALICE, "Also refreshed!"); dispatch(); 251 | } 252 | 253 | int main(int argc, char **argv) 254 | { 255 | OTRL_INIT; 256 | us = otrl_userstate_create(); 257 | 258 | otrl_privkey_read(us, "/home/iang/.gaim/otr.private_key"); 259 | otrl_instag_read(us, "inst.txt"); 260 | 261 | test(1,0); 262 | test(2,0); 263 | test(3,0); 264 | test(1,1); 265 | test(2,1); 266 | test_unreadable(); 267 | test_crash1(); 268 | test_refresh(3); 269 | test_refresh(2); 270 | test_refresh(1); 271 | 272 | otrl_userstate_free(us); 273 | 274 | return 0; 275 | } 276 | -------------------------------------------------------------------------------- /src/tlv.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew, 4 | * Nikita Borisov 5 | * 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of version 2.1 of the GNU Lesser General 9 | * Public License as published by the Free Software Foundation. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "tlv.h" 26 | 27 | /* Make a single TLV, copying the supplied data */ 28 | OtrlTLV *otrl_tlv_new(unsigned short type, unsigned short len, 29 | const unsigned char *data) 30 | { 31 | OtrlTLV *tlv = malloc(sizeof(OtrlTLV)); 32 | assert(tlv != NULL); 33 | tlv->type = type; 34 | tlv->len = len; 35 | tlv->data = malloc(len + 1); 36 | assert(tlv->data != NULL); 37 | memmove(tlv->data, data, len); 38 | tlv->data[tlv->len] = '\0'; 39 | tlv->next = NULL; 40 | return tlv; 41 | } 42 | 43 | /* Construct a chain of TLVs from the given data */ 44 | OtrlTLV *otrl_tlv_parse(const unsigned char *serialized, size_t seriallen) 45 | { 46 | OtrlTLV *tlv = NULL; 47 | OtrlTLV **tlvp = &tlv; 48 | while (seriallen >= 4) { 49 | unsigned short type = (serialized[0] << 8) + serialized[1]; 50 | unsigned short len = (serialized[2] << 8) + serialized[3]; 51 | serialized += 4; seriallen -=4; 52 | if (seriallen < len) break; 53 | *tlvp = otrl_tlv_new(type, len, serialized); 54 | serialized += len; 55 | seriallen -= len; 56 | tlvp = &((*tlvp)->next); 57 | } 58 | return tlv; 59 | } 60 | 61 | /* Deallocate a chain of TLVs */ 62 | void otrl_tlv_free(OtrlTLV *tlv) 63 | { 64 | while (tlv) { 65 | OtrlTLV *next = tlv->next; 66 | free(tlv->data); 67 | free(tlv); 68 | tlv = next; 69 | } 70 | } 71 | 72 | /* Find the serialized length of a chain of TLVs */ 73 | size_t otrl_tlv_seriallen(const OtrlTLV *tlv) 74 | { 75 | size_t totlen = 0; 76 | while (tlv) { 77 | totlen += tlv->len + 4; 78 | tlv = tlv->next; 79 | } 80 | return totlen; 81 | } 82 | 83 | /* Serialize a chain of TLVs. The supplied buffer must already be large 84 | * enough. */ 85 | void otrl_tlv_serialize(unsigned char *buf, const OtrlTLV *tlv) 86 | { 87 | while (tlv) { 88 | buf[0] = (tlv->type >> 8) & 0xff; 89 | buf[1] = tlv->type & 0xff; 90 | buf[2] = (tlv->len >> 8) & 0xff; 91 | buf[3] = tlv->len & 0xff; 92 | buf += 4; 93 | memmove(buf, tlv->data, tlv->len); 94 | buf += tlv->len; 95 | tlv = tlv->next; 96 | } 97 | } 98 | 99 | /* Return the first TLV with the given type in the chain, or NULL if one 100 | * isn't found. (The tlvs argument isn't const because the return type 101 | * needs to be non-const.) */ 102 | OtrlTLV *otrl_tlv_find(OtrlTLV *tlvs, unsigned short type) 103 | { 104 | while (tlvs) { 105 | if (tlvs->type == type) return tlvs; 106 | tlvs = tlvs->next; 107 | } 108 | return NULL; 109 | } 110 | -------------------------------------------------------------------------------- /src/tlv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Willy Lew, 4 | * Nikita Borisov 5 | * 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of version 2.1 of the GNU Lesser General 9 | * Public License as published by the Free Software Foundation. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #ifndef __TLV_H__ 22 | #define __TLV_H__ 23 | 24 | typedef struct s_OtrlTLV { 25 | unsigned short type; 26 | unsigned short len; 27 | unsigned char *data; 28 | struct s_OtrlTLV *next; 29 | } OtrlTLV; 30 | 31 | /* TLV types */ 32 | 33 | /* This is just padding for the encrypted message, and should be ignored. */ 34 | #define OTRL_TLV_PADDING 0x0000 35 | 36 | /* The sender has thrown away his OTR session keys with you */ 37 | #define OTRL_TLV_DISCONNECTED 0x0001 38 | 39 | /* The message contains a step in the Socialist Millionaires' Protocol. */ 40 | #define OTRL_TLV_SMP1 0x0002 41 | #define OTRL_TLV_SMP2 0x0003 42 | #define OTRL_TLV_SMP3 0x0004 43 | #define OTRL_TLV_SMP4 0x0005 44 | #define OTRL_TLV_SMP_ABORT 0x0006 45 | /* Like OTRL_TLV_SMP1, but there's a question for the buddy at the 46 | * beginning */ 47 | #define OTRL_TLV_SMP1Q 0x0007 48 | /* Tell the application the current "extra" symmetric key */ 49 | /* XXX: Document this in the protocol spec: 50 | * The body of the TLV will begin with a 4-byte indication of what this 51 | * symmetric key will be used for (file transfer, voice encryption, 52 | * etc.). After that, the contents are use-specific (which file, etc.). 53 | * There are no currently defined uses. */ 54 | #define OTRL_TLV_SYMKEY 0x0008 55 | 56 | /* Make a single TLV, copying the supplied data */ 57 | OtrlTLV *otrl_tlv_new(unsigned short type, unsigned short len, 58 | const unsigned char *data); 59 | 60 | /* Construct a chain of TLVs from the given data */ 61 | OtrlTLV *otrl_tlv_parse(const unsigned char *serialized, size_t seriallen); 62 | 63 | /* Deallocate a chain of TLVs */ 64 | void otrl_tlv_free(OtrlTLV *tlv); 65 | 66 | /* Find the serialized length of a chain of TLVs */ 67 | size_t otrl_tlv_seriallen(const OtrlTLV *tlv); 68 | 69 | /* Serialize a chain of TLVs. The supplied buffer must already be large 70 | * enough. */ 71 | void otrl_tlv_serialize(unsigned char *buf, const OtrlTLV *tlv); 72 | 73 | /* Return the first TLV with the given type in the chain, or NULL if one 74 | * isn't found. (The tlvs argument isn't const because the return type 75 | * needs to be non-const.) */ 76 | OtrlTLV *otrl_tlv_find(OtrlTLV *tlvs, unsigned short type); 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /src/userstate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander, 4 | * Willy Lew, Lisa Du, Nikita Borisov 5 | * 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of version 2.1 of the GNU Lesser General 9 | * Public License as published by the Free Software Foundation. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | /* system headers */ 22 | #include 23 | 24 | /* libotr headers */ 25 | #include "context.h" 26 | #include "privkey.h" 27 | #include "userstate.h" 28 | 29 | /* Create a new OtrlUserState. Most clients will only need one of 30 | * these. A OtrlUserState encapsulates the list of known fingerprints 31 | * and the list of private keys; if you have separate files for these 32 | * things for (say) different users, use different OtrlUserStates. If 33 | * you've got only one user, with multiple accounts all stored together 34 | * in the same fingerprint store and privkey store files, use just one 35 | * OtrlUserState. */ 36 | OtrlUserState otrl_userstate_create(void) 37 | { 38 | OtrlUserState us = malloc(sizeof(struct s_OtrlUserState)); 39 | if (!us) return NULL; 40 | us->context_root = NULL; 41 | us->privkey_root = NULL; 42 | us->instag_root = NULL; 43 | us->pending_root = NULL; 44 | us->timer_running = 0; 45 | return us; 46 | } 47 | 48 | /* Free a OtrlUserState. If you have a timer running for this userstate, 49 | stop it before freeing the userstate. */ 50 | void otrl_userstate_free(OtrlUserState us) 51 | { 52 | otrl_context_forget_all(us); 53 | otrl_privkey_forget_all(us); 54 | otrl_privkey_pending_forget_all(us); 55 | otrl_instag_forget_all(us); 56 | free(us); 57 | } 58 | -------------------------------------------------------------------------------- /src/userstate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander, 4 | * Willy Lew, Lisa Du, Nikita Borisov 5 | * 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of version 2.1 of the GNU Lesser General 9 | * Public License as published by the Free Software Foundation. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #ifndef __USERSTATE_H__ 22 | #define __USERSTATE_H__ 23 | 24 | typedef struct s_OtrlUserState* OtrlUserState; 25 | 26 | #include "instag.h" 27 | #include "context.h" 28 | #include "privkey-t.h" 29 | 30 | struct s_OtrlUserState { 31 | ConnContext *context_root; 32 | OtrlPrivKey *privkey_root; 33 | OtrlInsTag *instag_root; 34 | OtrlPendingPrivKey *pending_root; 35 | int timer_running; 36 | }; 37 | 38 | /* Create a new OtrlUserState. Most clients will only need one of 39 | * these. A OtrlUserState encapsulates the list of known fingerprints 40 | * and the list of private keys; if you have separate files for these 41 | * things for (say) different users, use different OtrlUserStates. If 42 | * you've got only one user, with multiple accounts all stored together 43 | * in the same fingerprint store and privkey store files, use just one 44 | * OtrlUserState. */ 45 | OtrlUserState otrl_userstate_create(void); 46 | 47 | /* Free a OtrlUserState. If you have a timer running for this userstate, 48 | stop it before freeing the userstate. */ 49 | void otrl_userstate_free(OtrlUserState us); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging library 3 | * Copyright (C) 2004-2014 Ian Goldberg, David Goulet, Rob Smits, 4 | * Chris Alexander, Willy Lew, Lisa Du, 5 | * Nikita Borisov 6 | * 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of version 2.1 of the GNU Lesser General 10 | * Public License as published by the Free Software Foundation. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public 18 | * License along with this library; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | 22 | #ifndef __VERSION_H__ 23 | #define __VERSION_H__ 24 | 25 | #define OTRL_VERSION "4.1.0" 26 | 27 | #define OTRL_VERSION_MAJOR 4 28 | #define OTRL_VERSION_MINOR 1 29 | #define OTRL_VERSION_SUB 0 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /test_suite/README: -------------------------------------------------------------------------------- 1 | OTR TEST SUITE 2 | 3 | BEFORE RUNNING 4 | This test suite is known to run with Python 2.7.1. The OTR C client first needs to compiled and statically linked with 5 | various versions of libotr. See the README file in "otr_c_client" for more information about this. 6 | 7 | TESTS 8 | Two tests are currently included. 9 | 10 | otr_test_general.py 11 | This test establishes OTR sessions between pairs of all 3.X versions of OTR clients. It also tests multiple 4.0 clients 12 | corresponding to one account establishing OTR sessions with another account with multiple 4.0 clients. 13 | 14 | otr_test_mixed.py 15 | This test establishes OTR sessions with multple 4.0 clients corresponding to one account with another accout with 16 | multiple 4.0 clients. This test also includes another 3.X with one of these accounts. All 3.X versions are tested. 17 | 18 | RUNNING 19 | Executing one of the above files directly will execute the corresponding test. The underlying instant messaging server 20 | will be started and stopped as necessary. 21 | 22 | After each test, "Test succeeded" or "Test failed" will be printed. 23 | -------------------------------------------------------------------------------- /test_suite/dummy_im.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import time 4 | import sys 5 | import threading 6 | import socket 7 | from struct import * 8 | 9 | 10 | class client_context(): 11 | def __init__(self, accountname, protocol, client_thread): 12 | self.accountname = accountname 13 | self.protocol = protocol 14 | self.thread = client_thread 15 | 16 | class client_thread(threading.Thread): 17 | def __init__(self, server, socket, debug=False): 18 | threading.Thread.__init__(self) 19 | self.server = server 20 | self.sock = socket 21 | self.done = False 22 | self.done_sem = threading.Semaphore() 23 | self.send_sem = threading.Semaphore() 24 | self.ctx = None 25 | self.debug = debug 26 | 27 | def run(self): 28 | #Registration: 1 byte accountname len, accountname, 1 byte protocol len, protocol 29 | accountname_len = self.read_1b_val() 30 | accountname = self.read_bytes(accountname_len) 31 | 32 | protocol_len = self.read_1b_val() 33 | protocol = self.read_bytes(protocol_len) 34 | 35 | if self.debug: 36 | print('Client accountname: ' + accountname + ' protocol: ' + protocol) 37 | 38 | #create client_context and add to table 39 | self.ctx = client_context(accountname, protocol, self) 40 | self.server.add_client(self.ctx) 41 | 42 | while not self.check_client_done(): 43 | try: #Message: 1 byte accountname len, accountname, 1 byte protocol len, protocol, 4 byte msg len, msg 44 | accountname_len = self.read_1b_val() 45 | accountname = self.read_bytes(accountname_len) 46 | 47 | protocol_len = self.read_1b_val() 48 | protocol = self.read_bytes(protocol_len) 49 | 50 | msg_len = self.read_4b_val() 51 | msg = self.read_bytes(msg_len) 52 | 53 | if not self.check_client_done(): 54 | self.server.deliver_msg(self.ctx.accountname, self.ctx.protocol, accountname, protocol, msg) 55 | 56 | except: 57 | if self.debug: 58 | print('removing client due to error') 59 | break 60 | 61 | self.server.remove_client(self.ctx) 62 | self.sock.close() 63 | 64 | def send_msg(self, msg): 65 | self.send_sem.acquire() 66 | totalsent = 0 67 | while not self.check_client_done() and totalsent < len(msg): 68 | sent = self.sock.send(msg[totalsent:]) 69 | if sent == 0: 70 | self.set_client_done() 71 | totalsent = totalsent + sent 72 | self.send_sem.release() 73 | if self.debug: 74 | print 'sent: ', totalsent 75 | 76 | def set_client_done(self): 77 | self.done_sem.acquire() 78 | self.done = True 79 | self.done_sem.release() 80 | if self.ctx is not None: 81 | if self.debug: 82 | print('Client accountname: ' + self.ctx.accountname + ' protocol: ' + self.ctx.protocol + ' is marked as done') 83 | else: 84 | if self.debug: 85 | print('Uninitialized client disconnected') 86 | 87 | def check_client_done(self): 88 | done_val = False 89 | self.done_sem.acquire() 90 | done_val = self.done 91 | self.done_sem.release() 92 | return done_val 93 | 94 | def read_bytes(self, num_bytes): 95 | msg = '' 96 | while not self.check_client_done() and len(msg) < num_bytes: 97 | chunk = self.sock.recv(num_bytes-len(msg)) 98 | if chunk == '': 99 | self.set_client_done() 100 | break 101 | msg = msg + chunk 102 | return msg 103 | 104 | def read_1b_val(self): 105 | byte = self.read_bytes(1) 106 | if len(byte) == 1: 107 | return ord(byte) 108 | else: return 109 | 110 | def read_4b_val(self): 111 | bytes = self.read_bytes(4) 112 | if len(bytes) == 4: 113 | val = unpack('!I',bytes) 114 | return int(val[0]) 115 | else: return 116 | 117 | 118 | class im_server(threading.Thread): 119 | def __init__(self, port, debug=False): 120 | threading.Thread.__init__(self) 121 | self.port = port 122 | self.finished = False 123 | self.clients_sem = threading.Semaphore() 124 | self.clients = dict() 125 | self.debug=debug 126 | 127 | def run(self): 128 | serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 129 | serversocket.bind(('', self.port)) 130 | serversocket.listen(50) 131 | 132 | if self.debug: 133 | print('Server listening...') 134 | 135 | while not self.finished: 136 | (clientsocket, address) = serversocket.accept() 137 | if self.debug: 138 | print('Client connected: ' + str(address)) 139 | cl_thread = client_thread(self, clientsocket, self.debug) 140 | cl_thread.daemon = True 141 | cl_thread.start() 142 | 143 | serversocket.close() 144 | 145 | def set_finished(self): 146 | self.finished = True 147 | 148 | def add_client(self, ctx): 149 | self.clients_sem.acquire() 150 | 151 | if self.debug: 152 | print('Adding client to dict: ' + str(ctx)) 153 | entry_list = self.clients.get((ctx.accountname, ctx.protocol)) 154 | if entry_list is None: 155 | entry_list = [ctx] 156 | self.clients[(ctx.accountname, ctx.protocol)] = entry_list 157 | else: 158 | entry_list.append(ctx) 159 | 160 | self.clients_sem.release() 161 | 162 | def remove_client(self, ctx): 163 | self.clients_sem.acquire() 164 | 165 | if self.debug: 166 | print('Removing client from dict: ' + str(ctx)) 167 | entry_list = self.clients.get((ctx.accountname, ctx.protocol)) 168 | if entry_list is not None and entry_list.count(ctx) > 0: 169 | entry_list.remove(ctx) 170 | if len(entry_list) == 0: 171 | del self.clients[(ctx.accountname, ctx.protocol)] 172 | 173 | self.clients_sem.release() 174 | 175 | def deliver_msg(self, src_accountname, src_protocol, dst_accountname, dst_protocol, msg): 176 | self.clients_sem.acquire() 177 | new_msg = bytearray() 178 | new_msg.append(len(src_accountname)) 179 | new_msg.extend(src_accountname) 180 | new_msg.append(len(src_protocol)) 181 | new_msg.extend(src_protocol) 182 | 183 | for b in pack('!I', len(msg)): 184 | new_msg.append(ord(b)) 185 | 186 | new_msg.extend(msg) 187 | 188 | entry_list = self.clients.get((dst_accountname, dst_protocol)) 189 | if entry_list is not None: 190 | for c in entry_list: 191 | if self.debug: 192 | print('Delivering msg from accountname: ' + src_accountname + 193 | ' protocol: ' + src_protocol + ' to accountname: ' + dst_accountname + 194 | ' protocol: ' + dst_protocol) 195 | c.thread.send_msg(new_msg) 196 | 197 | self.clients_sem.release() 198 | 199 | def main(argv): 200 | port = 1536 201 | 202 | if len(argv) > 0: 203 | port = int(argv[1]) 204 | 205 | server = im_server(port, debug=True) 206 | #server.daemon = True 207 | server.start() 208 | 209 | if __name__ == "__main__": 210 | main(sys.argv) 211 | 212 | -------------------------------------------------------------------------------- /test_suite/dummy_im/dummy_client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys 4 | import socket 5 | import threading 6 | from struct import * 7 | 8 | class kb_thread(threading.Thread): 9 | def __init__(self, socket): 10 | threading.Thread.__init__(self) 11 | self.sock = socket 12 | 13 | def run(self): 14 | while True: 15 | msg = getline(sys.stdin) 16 | 17 | account_end = msg.find(' ') 18 | if account_end < 0: continue 19 | accountname = msg[:account_end] 20 | 21 | proto_end = msg[account_end+1:].find(' ') + account_end + 1 22 | if proto_end < 0: continue 23 | protocol = msg[account_end+1:proto_end] 24 | 25 | msg = msg[proto_end+1:] 26 | 27 | print("Sending msg -- account: " + accountname + " protocol: " + protocol + " msg: " + msg) 28 | 29 | send_msg(self.sock, accountname, protocol, msg) 30 | 31 | def getline(stream, delimiter="\n"): 32 | def _gen(): 33 | while 1: 34 | line = stream.readline() 35 | if delimiter in line: 36 | yield line[0:line.index(delimiter)] 37 | break 38 | else: 39 | yield line 40 | return "".join(_gen()) 41 | 42 | def send(sock, msg, length=0): 43 | if length == 0: length = len(msg) 44 | totalsent = 0 45 | while totalsent < length: 46 | sent = sock.send(msg[totalsent:]) 47 | if sent == 0: 48 | return 49 | totalsent = totalsent + sent 50 | 51 | def read_bytes(sock, num_bytes): 52 | msg = '' 53 | while len(msg) < num_bytes: 54 | chunk = sock.recv(num_bytes-len(msg)) 55 | if chunk == '': 56 | return 57 | msg = msg + chunk 58 | return msg 59 | 60 | def read_1b_val(sock): 61 | byte = read_bytes(sock, 1) 62 | if len(byte) == 1: 63 | return ord(byte) 64 | else: return 65 | 66 | def read_4b_val(sock): 67 | bytes = read_bytes(sock, 4) 68 | if len(bytes) == 4: 69 | val = unpack('!I',bytes) 70 | return int(val[0]) 71 | else: return 72 | 73 | def recv_msg(sock): 74 | recv_acc_len = read_1b_val(sock) 75 | recv_acc = read_bytes(sock, recv_acc_len) 76 | recv_proto_len = read_1b_val(sock) 77 | recv_proto = read_bytes(sock, recv_proto_len) 78 | recv_msg_len = read_4b_val(sock) 79 | recv_msg = read_bytes(sock, recv_msg_len) 80 | print("Received msg: " + recv_msg) 81 | return recv_msg 82 | 83 | def send_msg(sock, accountname, protocol, msg): 84 | new_msg = bytearray() 85 | new_msg.append(len(accountname)) 86 | new_msg.extend(accountname) 87 | new_msg.append(len(protocol)) 88 | new_msg.extend(protocol) 89 | for b in pack('!I', len(msg)): 90 | new_msg.append(ord(b)) 91 | new_msg.extend(msg) 92 | send(sock, new_msg) 93 | 94 | def main(argv): 95 | if len(argv) < 3: 96 | print("Usage: " + argv[0] + " ") 97 | return 98 | 99 | accountname = argv[1] 100 | protocol = argv[2] 101 | 102 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 103 | s.connect(("localhost", 1536)) 104 | 105 | acc_len = len(accountname) 106 | packed_acc_len = pack('B', acc_len) 107 | 108 | #init 109 | send(s, packed_acc_len, 1) 110 | send(s, accountname) 111 | send(s, pack('B', len(protocol)), 1) 112 | send(s, protocol) 113 | 114 | input_handler = kb_thread(s) 115 | input_handler.start() 116 | 117 | while True: 118 | recv_msg(s) 119 | 120 | if __name__ == "__main__": 121 | main(sys.argv) 122 | 123 | -------------------------------------------------------------------------------- /test_suite/instance_tags0.txt: -------------------------------------------------------------------------------- 1 | otrtest1 prpl-aim 27e31597 2 | otrtest2 prpl-aim 27e31598 3 | otrtest3 prpl-aim 27e31599 4 | -------------------------------------------------------------------------------- /test_suite/instance_tags1.txt: -------------------------------------------------------------------------------- 1 | otrtest1 prpl-aim 27e32500 2 | otrtest2 prpl-aim 27e32501 3 | otrtest3 prpl-aim 27e32502 4 | -------------------------------------------------------------------------------- /test_suite/instance_tags2.txt: -------------------------------------------------------------------------------- 1 | otrtest1 prpl-aim 27e32603 2 | otrtest2 prpl-aim 27e32604 3 | otrtest3 prpl-aim 27e32605 4 | -------------------------------------------------------------------------------- /test_suite/instance_tags3.txt: -------------------------------------------------------------------------------- 1 | otrtest1 prpl-aim 27e33506 2 | otrtest2 prpl-aim 27e33507 3 | otrtest3 prpl-aim 27e33508 4 | -------------------------------------------------------------------------------- /test_suite/instance_tags4.txt: -------------------------------------------------------------------------------- 1 | otrtest1 prpl-aim 27e42509 2 | otrtest2 prpl-aim 27e42500 3 | otrtest3 prpl-aim 27e42501 4 | -------------------------------------------------------------------------------- /test_suite/otr.private_key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/off-the-record/libotr/c9710e9b5bbd2f9fd540c6f4fe040937444d20e2/test_suite/otr.private_key -------------------------------------------------------------------------------- /test_suite/otr_c_client/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc dummy_client.c -DOTR30 -g -o dummy_client_30 -Wl,-Bstatic -I/home/rdfsmits/otr/cvs-3.0/libotr/src -L./libs -lotr3.0 -Wl,-Bdynamic -lpthread -lgcrypt -lgpg-error 3 | gcc dummy_client.c -DOTR31 -g -o dummy_client_31 -Wl,-Bstatic -I/home/rdfsmits/otr/cvs-3.1/libotr/src -L./libs -lotr3.1 -Wl,-Bdynamic -lpthread -lgcrypt -lgpg-error 4 | gcc dummy_client.c -DOTR32 -g -o dummy_client_32 -Wl,-Bstatic -I/home/rdfsmits/otr/cvs-3.2/libotr/src -L./libs -lotr3.2 -Wl,-Bdynamic -lpthread -lgcrypt -lgpg-error 5 | gcc dummy_client.c -DOTR40 -g -o dummy_client_40 -Wl,-Bstatic -I/home/rdfsmits/otr/git/otr/libotr/src -L./libs -lotr4.0 -Wl,-Bdynamic -lpthread -lgcrypt -lgpg-error 6 | clean: 7 | rm dummy_client_30 dummy_client_31 dummy_client_32 dummy_client_40 8 | 9 | -------------------------------------------------------------------------------- /test_suite/otr_c_client/README: -------------------------------------------------------------------------------- 1 | The Makefile is currently set up to find pre-compiled libotr static libraries in a ./libs directory. 2 | You will need to change the header include directories as appropriate. 3 | 4 | It expects the following files (copied and renamed from the appropriate versions): 5 | 6 | libotr3.0.a 7 | libotr3.1.a 8 | libotr3.2.a 9 | libotr4.0.a 10 | 11 | -------------------------------------------------------------------------------- /test_suite/otr_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys 4 | import itertools 5 | import re 6 | import Queue 7 | 8 | from dummy_im import * 9 | from otr_subprocess import * 10 | 11 | client_location_30 = "./otr_c_client/dummy_client_30" 12 | client_location_31 = "./otr_c_client/dummy_client_31" 13 | client_location_32 = "./otr_c_client/dummy_client_32" 14 | client_location_40 = "./otr_c_client/dummy_client_40" 15 | 16 | client_locations = [client_location_30, client_location_31, client_location_32, client_location_40] 17 | 18 | 19 | class otr_test_failed_exception(Exception): 20 | def __init__(self, value, p_list=None): 21 | self.value = value 22 | self.p_list = p_list 23 | if p_list is not None: 24 | for p in p_list: 25 | q_id = p.send_get_contexts() 26 | serialized_contexts = p.get_query_blocking(q_id) 27 | p_contexts = deserialize_contexts(serialized_contexts) 28 | print("Dumping contexts:") 29 | dump_contexts(p_contexts) 30 | print("Dumping raw messages:") 31 | dump_msg_queue(p.querymap[q_raw_msg]) 32 | print("Dumping otr-processed messages:") 33 | dump_msg_queue(p.querymap[q_otr_msg]) 34 | print("Dumping error messages:") 35 | dump_queue(p.querymap[q_err]) 36 | 37 | def __str__(self): 38 | return "Test Failed: " + repr(self.value) 39 | 40 | class otr_test: 41 | def __init__(self, alices, bobs): 42 | 43 | self.alices = alices 44 | self.bobs = bobs 45 | self.subprocesses = alices + bobs 46 | 47 | #no default tests -- always defined by child class 48 | def run_test(self, options={}): 49 | return 50 | 51 | def reset_processes(self): 52 | for p in self.subprocesses: 53 | p.sigint() 54 | 55 | #Give them a moment to terminate on their own if they are willing and able 56 | time.sleep(0.5) 57 | 58 | for p in self.subprocesses: 59 | p.reset() 60 | 61 | def check_error_all(self, allowed_events=[]): 62 | for p in self.subprocesses: 63 | p.check_error() 64 | if not p.querymap[q_err].empty(): 65 | tmp_stack = [] 66 | 67 | while not p.querymap[q_err].empty(): 68 | item = p.querymap[q_err].get(False) 69 | if allowed_events.count(item) < 1: 70 | raise otr_test_failed_exception(item, [p]) 71 | tmp_stack.append(item) 72 | 73 | while len(tmp_stack) > 0: 74 | p.querymap[q_err].put(tmp_stack.pop()) 75 | 76 | 77 | def wait_all_gone_encrypted(self): 78 | for p in self.subprocesses: 79 | p.get_query_blocking(q_gone_secure) #Wait for "gone encrypted" signal 80 | print(p.accountname + " " + p.protocol + " went encrypted") 81 | 82 | def send_encrypted_and_check(self, senders, receivers, out_msg, options={}): 83 | for p in list(set(senders)): 84 | print("About to send encrypted message") 85 | msg_id = p.send_msg(receivers[0].accountname, receivers[0].protocol, out_msg) 86 | modified_msg = p.get_query_blocking(msg_id) 87 | print("Encrypted message sent") 88 | 89 | if modified_msg.find(out_msg) >= 0: 90 | raise otr_test_failed_exception("Sent message not encrypted", self.subprocesses) 91 | 92 | receiver_msgs = [] 93 | 94 | for p in list(set(receivers)): #remove duplicates 95 | print("About to receive raw encrypted message") 96 | receiver_msgs.append(get_n_messages_blocking(p.querymap[q_raw_data_msg], len(list(set(senders))))) 97 | 98 | for msg in receiver_msgs: 99 | check_contains_message_matches_ex(msg, otr_data_prefix_regex, len(list(set(senders))), self.subprocesses) 100 | 101 | for i in range(len(list(set(senders)))): 102 | print("About to receive decrypted message") 103 | receiver_recvd = find_first_msg_blocking(receivers, q_otr_msg) 104 | 105 | if not check_message_matches(receiver_recvd, re.escape(out_msg)): 106 | raise otr_test_failed_exception("Received message not decrypted", self.subprocesses) 107 | 108 | def check_all_contexts_encrypted(self, options={}): 109 | for p in self.alices: 110 | c_id = p.send_get_contexts() 111 | serialized_contexts = p.get_query_blocking(c_id) 112 | p_contexts = deserialize_contexts(serialized_contexts) 113 | check_contexts_for_encrypted(p_contexts, self.num_bob, p) 114 | 115 | for p in self.bobs: 116 | c_id = p.send_get_contexts() 117 | serialized_contexts = p.get_query_blocking(c_id) 118 | p_contexts = deserialize_contexts(serialized_contexts) 119 | check_contexts_for_encrypted(p_contexts, self.num_alice, p) 120 | 121 | def init_processes(self, options={}): 122 | for p in self.subprocesses: 123 | p.check_error() 124 | 125 | privkey = options.get('privkey', 'otr.private_key') 126 | 127 | for p in self.subprocesses: 128 | c_id = p.send_read_privkey(privkey) 129 | p.get_query_blocking(c_id) 130 | 131 | for i, p in enumerate(self.alices): 132 | c_id = p.send_read_instag("instance_tags" + str(i) + ".txt") 133 | p.get_query_blocking(c_id) 134 | 135 | for i, p in enumerate(self.bobs): 136 | c_id = p.send_read_instag("instance_tags" + str(i) + ".txt") 137 | p.get_query_blocking(c_id) 138 | 139 | for p in self.subprocesses: 140 | c_id = p.send_init() 141 | p.get_query_blocking(c_id) 142 | 143 | for p in self.subprocesses: 144 | p.check_error() 145 | 146 | def otr_init_msg(self, options={}): 147 | alice_init_idx = options.get('alice_init_idx', 0) 148 | msg_id = self.alices[alice_init_idx].send_msg(self.bob_account, self.bob_proto, self.msg1) 149 | modified_msg = self.alices[alice_init_idx].get_query_blocking(msg_id) 150 | done = self.alices[alice_init_idx].get_query_blocking(msg_id) 151 | 152 | for p in self.bobs: 153 | msg = p.get_query_blocking(q_otr_msg) 154 | if check_message_matches(msg, re.escape(self.msg1) + otr_tab_tag): 155 | raise otr_test_failed_exception("Query tabs not removed", self.subprocesses) 156 | 157 | check_message_matches_ex(msg, re.escape(self.msg1), self.subprocesses) 158 | 159 | def otr_init_query(self, options={}): 160 | msg_id = self.alices[0].send_msg(self.bob_account, self.bob_proto, otr_query) 161 | modified_msg = self.alices[0].get_query_blocking(msg_id) 162 | done = "" 163 | while done.find("DONE") < 0: 164 | done = self.alices[0].get_query_blocking(msg_id) 165 | 166 | #More to check? 167 | 168 | #TODO: Make these methods class methods / static methods? 169 | def chomp_auth_msgs(ps): 170 | for p in ps: 171 | msgs = get_n_messages_blocking(p.querymap[q_raw_msg], p.querymap[q_raw_msg].qsize()) 172 | for msg in msgs: 173 | parsed_msg = deserialize_msg(msg)[2] 174 | if parsed_msg.find("?OTR") != 0: 175 | p.querymap[q_raw_msg].put(msg) 176 | 177 | def chomp_msgs(ps, q_idx): 178 | for p in ps: 179 | get_n_messages_blocking(p.querymap[q_idx], p.querymap[q_idx].qsize()) 180 | 181 | 182 | def wait_gone_encrypted(ps): 183 | for p in ps: 184 | p.get_query_blocking(q_gone_secure) #Wait for "gone encrypted" signal 185 | 186 | def find_first_msg_blocking(ps, q_idx): 187 | #We have to spin here until this is implemented: 188 | # http://bugs.python.org/issue3831 189 | result = None 190 | 191 | while result is None: 192 | for p in ps: 193 | try: 194 | result = p.querymap[q_idx].get(True, 0.05) #block for 50ms 195 | break 196 | except (Queue.Empty) as e: 197 | continue 198 | 199 | return result 200 | 201 | def check_message_matches(msg, regex): 202 | msg = deserialize_msg(msg) 203 | return re.match(regex, msg[2]) is not None 204 | 205 | def check_message_matches_ex(msg, regex, p_list): 206 | if not check_message_matches(msg, regex): 207 | raise otr_test_failed_exception("msg failed to match regex -- regex: " + regex + " msg: " + msg[2], p_list) 208 | 209 | def check_contains_message_matches_ex(msgs, regex, n, p_list): 210 | matches = 0 211 | for msg in msgs: 212 | if check_message_matches(msg, regex): 213 | matches += 1 214 | 215 | if matches < n: 216 | raise otr_test_failed_exception("msg failed to match regex " + str(n) + " times -- regex: " + regex + " matches: " + str(matches) + " msgs: " + str(msgs), p_list) 217 | 218 | 219 | #Given a process's list of contexts, check that num_expected_encrypted are encrypted 220 | def check_contexts_for_encrypted(contexts, num_expected_encrypted, p): 221 | num_good = 0 222 | 223 | if num_expected_encrypted > 1 and p.client_location != client_location_40: #XXX: support future versions 224 | raise otr_test_failed_exception("Expected > 1 good contexts on protocol version < 2", [p]) 225 | 226 | if len(contexts) == 0: 227 | raise otr_test_failed_exception("Failed: No contexts", [p]) 228 | 229 | if p.client_location != client_location_40 and num_expected_encrypted == 1 and len(contexts) == 1: 230 | if not check_context_encrypted(contexts[0], p): 231 | raise otr_test_failed_exception("Failed: Not encrypted", [p]) 232 | 233 | else: 234 | for context in contexts: 235 | if check_context_encrypted(context, p): 236 | num_good += 1 237 | 238 | if num_good < num_expected_encrypted: 239 | msg = "Failed: fewer than expected encrypted contexts. Found " + str(num_good) + " Expected " + str(num_expected_encrypted) 240 | raise otr_test_failed_exception(msg, [p]) 241 | 242 | def check_offer_accepted(context): 243 | if context.offer_state != 3: 244 | raise otr_test_failed_exception("Failed: Offer not given or not accepted") 245 | 246 | def check_context_encrypted(context, p): 247 | if context.msg_state == 1: 248 | return True 249 | else: 250 | return False 251 | -------------------------------------------------------------------------------- /test_suite/otr_test_general.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys 4 | import itertools 5 | import re 6 | import Queue 7 | 8 | from otr_test import * 9 | 10 | class otr_test_general(otr_test): 11 | #Supports multiple 4.0 clients or single 3.X clients 12 | 13 | def __init__(self, alices, bobs): 14 | otr_test.__init__(self, alices, bobs) 15 | 16 | self.alice_account = alices[0].accountname 17 | self.alice_proto = alices[0].protocol 18 | 19 | self.bob_account = bobs[0].accountname 20 | self.bob_proto = bobs[0].protocol 21 | 22 | self.num_alice = len(self.alices) 23 | self.num_bob = len(self.bobs) 24 | 25 | def run_test(self, options={}): 26 | try: 27 | self.msg1 = options.get('msg1', '%$sup?') 28 | self.msg2 = options.get('msg2', '^¬ much') 29 | self.msg3 = options.get('msg3', '*(cool') 30 | self.init_processes(options) 31 | time.sleep(1) 32 | print("Processes initialized") 33 | if options.get('otr_init_method', 'msg') == 'msg': 34 | self.otr_init_msg(options) 35 | elif options.get('otr_init_method', 'msg') == 'query': 36 | self.otr_init_query() 37 | print("About to wait for all to go encrypted") 38 | self.wait_all_gone_encrypted() 39 | print("All went encrypted") 40 | #self.analyze_otr_init(options) #hasn't been updated to support fragments 41 | time.sleep(5) 42 | self.check_all_contexts_encrypted(options) 43 | print("Verified encrypted contexts") 44 | self.send_encrypted_and_check(self.bobs, self.alices, self.msg2, options) 45 | print("Sent and verified encrypted message from bobs to alices") 46 | self.send_encrypted_and_check(self.alices, self.bobs, self.msg3, options) 47 | print("Sent and verified encrypted message from alices to bobs") 48 | self.check_error_all(options.get('allowed_msg_events', [])) 49 | print("Test succeeded") 50 | self.reset_processes() 51 | 52 | except (subprocess_exception, otr_test_failed_exception) as e: 53 | print '***Exception: ', e, e.value, sys.exc_info() 54 | self.reset_processes() 55 | print("Test failed") 56 | 57 | def analyze_otr_init(self, options={}): 58 | alice_msgs = [] 59 | bob_msgs = [] 60 | 61 | for p in self.bobs: 62 | init_msg = p.querymap[q_raw_msg].get() 63 | 64 | if options.get('otr_init_method', 'msg') == 'msg': 65 | check_contains_message_matches_ex([init_msg], re.escape(self.msg1) + otr_tab_tag, 1, self.subprocesses) 66 | elif options.get('otr_init_method', 'msg') == 'query': 67 | check_contains_message_matches_ex([init_msg], re.escape(otr_query), 1, self.subprocesses) 68 | 69 | bob_msgs.append(get_n_messages_blocking(p.querymap[q_raw_auth_msg], self.num_alice*self.num_bob*2)) 70 | 71 | for p in self.alices: 72 | alice_msgs.append(get_n_messages_blocking(p.querymap[q_raw_auth_msg], self.num_bob + self.num_bob*self.num_alice)) 73 | 74 | for msg in bob_msgs: 75 | check_contains_message_matches_ex(msg, otr_key_prefix_regex, self.num_alice*self.num_bob, self.subprocesses) 76 | check_contains_message_matches_ex(msg, otr_sign_prefix_regex, self.num_alice*self.num_bob, self.subprocesses) 77 | 78 | for msg in alice_msgs: 79 | check_contains_message_matches_ex(msg, otr_commit_prefix_regex, self.num_bob, self.subprocesses) 80 | check_contains_message_matches_ex(msg, otr_reveal_prefix_regex, self.num_bob*self.num_alice, self.subprocesses) 81 | 82 | 83 | def test_all_vers_1_1(): 84 | alice_accountname = "otrtest3" 85 | bob_accountname = "otrtest1" 86 | protocol = "prpl-aim" 87 | 88 | test_combos = [] 89 | 90 | for loc1, loc2 in itertools.product(client_locations, repeat=2): 91 | test_combos.append((loc1, loc2)) 92 | 93 | for i, (loc1, loc2) in enumerate(test_combos): #otr_subprocess([location, account, protocol, im_ip, im_port, log_tag+str(i)+".txt"], 0) 94 | alice = otr_subprocess([loc1, alice_accountname, protocol, im_ip, im_port, "alice"+str(i)+".txt"], 0) 95 | bob = otr_subprocess([loc2, bob_accountname, protocol, im_ip, im_port, "bob"+str(i)+".txt"], 0) 96 | 97 | print('Testing ' + loc1 + ' and ' + loc2) 98 | 99 | the_test = otr_test_general([alice], [bob]) 100 | options={} 101 | options['otr_init_method'] = 'msg' 102 | the_test.run_test(options) 103 | time.sleep(1) 104 | 105 | print('Test complete!') 106 | 107 | def test_basic_40(): 108 | #tests single 4.0s 109 | alice_accountname = "otrtest3" 110 | bob_accountname = "otrtest1" 111 | protocol = "prpl-aim" 112 | 113 | alices = get_many_instances(client_location_40, alice_accountname, protocol, 1, "alice") 114 | bobs = get_many_instances(client_location_40, bob_accountname, protocol, 1, "bob") 115 | 116 | the_test = otr_test_general(alices, bobs) 117 | options={} 118 | options['otr_init_method'] = 'query' 119 | options['allowed_msg_events'] = ["OTRL_MSGEVENT_LOG_HEARTBEAT_SENT", "OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD"] 120 | the_test.run_test(options) 121 | 122 | print('Test complete!') 123 | 124 | 125 | def test_multi_40(): 126 | #tests multiple 4.0 versions 127 | alice_accountname = "otrtest3" 128 | bob_accountname = "otrtest1" 129 | protocol = "prpl-aim" 130 | 131 | alices = get_many_instances(client_location_40, alice_accountname, protocol, 3, "alice") 132 | bobs = get_many_instances(client_location_40, bob_accountname, protocol, 2, "bob") 133 | 134 | the_test = otr_test_general(alices, bobs) 135 | options={} 136 | options['otr_init_method'] = 'query' 137 | options['allowed_msg_events'] = ["OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE", "OTRL_MSGEVENT_LOG_HEARTBEAT_SENT", "OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD"] 138 | the_test.run_test(options) 139 | 140 | print('Test complete!') 141 | 142 | 143 | def main(args): 144 | server = im_server(int(im_port)) 145 | server.daemon = True 146 | server.start() 147 | 148 | print('Testing basic 4.0 to 4.0') 149 | test_basic_40() 150 | 151 | print('Testing all versions 1 client to 1 client') 152 | test_all_vers_1_1() 153 | 154 | print('Testing multi 4.0 to multi 4.0') 155 | test_multi_40() 156 | 157 | server.set_finished() 158 | print("Shutting down...") 159 | 160 | if __name__ == "__main__": 161 | main(sys.argv) 162 | 163 | -------------------------------------------------------------------------------- /test_suite/otr_test_mixed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys 4 | import itertools 5 | import re 6 | import Queue 7 | 8 | from otr_test import * 9 | 10 | class otr_test_mixed(otr_test): 11 | #Supports multiple 4.0 clients or single 3.X clients 12 | 13 | def __init__(self, alices, bobs): 14 | otr_test.__init__(self, alices, bobs) 15 | 16 | self.alice_account = alices[0].accountname 17 | self.alice_proto = alices[0].protocol 18 | 19 | self.bob_account = bobs[0].accountname 20 | self.bob_proto = bobs[0].protocol 21 | 22 | self.num_alice = len(self.alices) 23 | self.num_bob = len(self.bobs) 24 | 25 | def run_test(self, options={}): 26 | try: 27 | self.msg1 = options.get('msg1', '%$sup?') 28 | self.msg2 = options.get('msg2', '^¬ much') 29 | self.msg3 = options.get('msg3', '*(cool') 30 | 31 | self.init_processes(options) 32 | 33 | if options.get('otr_init_method', 'msg') == 'msg': 34 | self.otr_init_msg(options) 35 | elif options.get('otr_init_method', 'msg') == 'query': 36 | self.otr_init_query() 37 | 38 | alice_encrypted_idx = options.get('alice_expected_encrypted_idx', range(len(self.alices))) 39 | bob_encrypted_idx = options.get('bob_expected_encrypted_idx', range(len(self.bobs))) 40 | 41 | alice_encrypted = [] 42 | for i in alice_encrypted_idx: 43 | alice_encrypted.append(self.alices[i]) 44 | 45 | bob_encrypted = [] 46 | for i in bob_encrypted_idx: 47 | bob_encrypted.append(self.bobs[i]) 48 | 49 | 50 | wait_gone_encrypted(alice_encrypted + bob_encrypted) 51 | time.sleep(1) #Ensure all messages received and processed 52 | 53 | chomp_msgs(alice_encrypted + bob_encrypted, q_raw_msg) 54 | 55 | #Check contexts 56 | for p in list(set(alice_encrypted)): 57 | c_id = p.send_get_contexts() 58 | serialized_contexts = p.get_query_blocking(c_id) 59 | p_contexts = deserialize_contexts(serialized_contexts) 60 | check_contexts_for_encrypted(p_contexts, alice_encrypted.count(p), p) 61 | 62 | 63 | for p in list(set(bob_encrypted)): 64 | c_id = p.send_get_contexts() 65 | serialized_contexts = p.get_query_blocking(c_id) 66 | p_contexts = deserialize_contexts(serialized_contexts) 67 | check_contexts_for_encrypted(p_contexts, bob_encrypted.count(p), p) 68 | 69 | 70 | #We only send messages between the 4.0s because the 3.X will be paired with only one partner, and we don't know 71 | #for sure which one. 72 | self.send_encrypted_and_check(return_only_40(bob_encrypted), return_only_40(alice_encrypted), self.msg2, options) 73 | 74 | self.send_encrypted_and_check(return_only_40(alice_encrypted), return_only_40(bob_encrypted), self.msg3, options) 75 | 76 | #Non-4.0 clients will error from unexpected messages, but we are expecting this 77 | for p in self.subprocesses: 78 | p.check_error() 79 | if not p.querymap[q_err].empty() and p.client_location == client_location_40: 80 | errors = get_n_messages_blocking(p.querymap[q_err], p.querymap[q_err].qsize()) 81 | for error in errors: 82 | print("Warning: setup errors detected: " + error) 83 | 84 | print("Test succeeded") 85 | self.reset_processes() 86 | 87 | except (subprocess_exception, otr_test_failed_exception) as e: 88 | print '***Exception: ', e.value, sys.exc_info() 89 | self.reset_processes() 90 | print("Test failed") 91 | 92 | def return_only_40(processes): 93 | result = [] 94 | for p in processes: 95 | if p.client_location == client_location_40: 96 | result.append(p) 97 | 98 | return result 99 | 100 | def test_40_mixed(num_alice_40, num_bob_40, alice_extra_location=None, bob_extra_location=None, options={}): 101 | alice_accountname = "otrtest3" 102 | bob_accountname = "otrtest1" 103 | protocol = "prpl-aim" 104 | 105 | alices = get_many_instances(client_location_40, alice_accountname, protocol, num_alice_40, "alice") 106 | bobs = get_many_instances(client_location_40, bob_accountname, protocol, num_bob_40, "bob") 107 | 108 | if alice_extra_location is not None: 109 | alices.append(otr_subprocess([alice_extra_location, alice_accountname, protocol, im_ip, im_port, "alice" + str(num_alice_40) + ".txt"], 0)) 110 | 111 | if bob_extra_location is not None: 112 | bobs.append(otr_subprocess([bob_extra_location, bob_accountname, protocol, im_ip, im_port, "bob" + str(num_bob_40) + ".txt"], 0)) 113 | 114 | the_test = otr_test_mixed(alices, bobs) 115 | 116 | options['otr_init_method'] = 'msg' 117 | options['allowed_msg_events'] = ["OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE", "OTRL_MSGEVENT_LOG_HEARTBEAT_SENT", "OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD"] 118 | the_test.run_test(options) 119 | 120 | 121 | def main(args): 122 | print('Testing 4.0 and other to 4.0') 123 | server = im_server(int(im_port)) 124 | server.daemon = True 125 | server.start() 126 | 127 | options = {} 128 | options['alice_init_idx'] = 0 129 | options['alice_expected_encrypted_idx'] = [0, 0, 1, 1, 2, 2] 130 | options['bob_expected_encrypted_idx'] = [0, 0, 0, 1, 1, 1, 2] 131 | 132 | for location in client_locations: 133 | if location == client_location_40: continue 134 | print("Testing with extra Bob location: " + location) 135 | test_40_mixed(3, 2, None, location, options) 136 | time.sleep(1) 137 | 138 | 139 | options['alice_expected_encrypted_idx'] = [0, 1] 140 | options['bob_expected_encrypted_idx'] = [0, 0, 1, 1] 141 | 142 | for location in client_locations: 143 | if location == client_location_40: continue 144 | print("Testing with extra Alice location: " + location) 145 | test_40_mixed(2, 2, location, None, options) 146 | time.sleep(1) 147 | 148 | server.set_finished() 149 | print("Shutting down...") 150 | 151 | if __name__ == "__main__": 152 | main(sys.argv) 153 | 154 | -------------------------------------------------------------------------------- /toolkit/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CPPFLAGS = -I$(includedir) -I../src @LIBGCRYPT_CFLAGS@ 2 | 3 | noinst_HEADERS = aes.h ctrmode.h parse.h sesskeys.h readotr.h sha1hmac.h 4 | 5 | bin_PROGRAMS = otr_parse otr_sesskeys otr_mackey otr_readforge \ 6 | otr_modify otr_remac 7 | 8 | COMMON_S = parse.c sha1hmac.c 9 | COMMON_LD = ../src/libotr.la @LIBS@ @LIBGCRYPT_LIBS@ 10 | 11 | otr_parse_SOURCES = otr_parse.c readotr.c $(COMMON_S) 12 | otr_parse_LDADD = $(COMMON_LD) 13 | 14 | otr_sesskeys_SOURCES = otr_sesskeys.c sesskeys.c $(COMMON_S) 15 | otr_sesskeys_LDADD = $(COMMON_LD) 16 | 17 | otr_mackey_SOURCES = otr_mackey.c sesskeys.c $(COMMON_S) 18 | otr_mackey_LDADD = $(COMMON_LD) 19 | 20 | otr_readforge_SOURCES = otr_readforge.c readotr.c sesskeys.c \ 21 | aes.c ctrmode.c $(COMMON_S) 22 | otr_readforge_LDADD = $(COMMON_LD) 23 | 24 | otr_modify_SOURCES = otr_modify.c readotr.c $(COMMON_S) 25 | otr_modify_LDADD = $(COMMON_LD) 26 | 27 | otr_remac_SOURCES = otr_remac.c $(COMMON_S) 28 | otr_remac_LDADD = $(COMMON_LD) 29 | 30 | 31 | man_MANS = otr_toolkit.1 32 | EXTRA_DIST = otr_toolkit.1 33 | 34 | MANLINKS = otr_parse.1 otr_sesskeys.1 otr_mackey.1 otr_readforge.1 \ 35 | otr_modify.1 otr_remac.1 36 | 37 | install-data-local: 38 | -mkdir -p $(DESTDIR)$(man1dir) 39 | (cd $(DESTDIR)$(man1dir) && \ 40 | for f in $(MANLINKS); do ln -sf otr_toolkit.1 $$f; done) 41 | 42 | uninstall-local: 43 | (cd $(DESTDIR)$(man1dir) && \ 44 | for f in $(MANLINKS); do rm -f $$f; done) 45 | -------------------------------------------------------------------------------- /toolkit/aes.h: -------------------------------------------------------------------------------- 1 | /* Retrieved from http://www.cr0.net:8040/code/crypto/aes/aes.h */ 2 | 3 | #ifndef _AES_H 4 | #define _AES_H 5 | 6 | #ifndef uint8 7 | #define uint8 unsigned char 8 | #endif 9 | 10 | #ifndef uint32 11 | #define uint32 unsigned long int 12 | #endif 13 | 14 | typedef struct 15 | { 16 | uint32 erk[64]; /* encryption round keys */ 17 | uint32 drk[64]; /* decryption round keys */ 18 | int nr; /* number of rounds */ 19 | } 20 | aes_context; 21 | 22 | int aes_set_key( aes_context *ctx, uint8 *key, int nbits ); 23 | void aes_encrypt( aes_context *ctx, uint8 input[16], uint8 output[16] ); 24 | void aes_decrypt( aes_context *ctx, uint8 input[16], uint8 output[16] ); 25 | 26 | #endif /* aes.h */ 27 | -------------------------------------------------------------------------------- /toolkit/ctrmode.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging Toolkit 3 | * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov 4 | * 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of version 2 of the GNU General Public License as 8 | * published by the Free Software Foundation. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | /* system headers */ 21 | #include 22 | #include 23 | 24 | /* toolkit headers */ 25 | #include "aes.h" 26 | 27 | /* Encrypt or decrypt data in AES-CTR mode. (The operations are the 28 | * same.) We roll our own here just to double-check that the calls 29 | * libotr makes to libgcrypt are doing the right thing. */ 30 | void aes_ctr_crypt(unsigned char *out, const unsigned char *in, size_t len, 31 | unsigned char key[16], unsigned char ctrtop[8]) 32 | { 33 | unsigned char ctr[16], encctr[16]; 34 | aes_context aesc; 35 | 36 | aes_set_key(&aesc, key, 128); 37 | 38 | memmove(ctr, ctrtop, 8); 39 | memset(ctr+8, 0, 8); 40 | 41 | while(len > 0) { 42 | /* How much to do at a time? */ 43 | size_t i; 44 | size_t amt = len; 45 | if (amt > 16) amt = 16; 46 | aes_encrypt(&aesc, ctr, encctr); 47 | for(i=0;i0;--i) { 53 | if (++ctr[i-1] != 0) break; 54 | } 55 | 56 | out += amt; 57 | in += amt; 58 | len -= amt; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /toolkit/ctrmode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging Toolkit 3 | * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov 4 | * 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of version 2 of the GNU General Public License as 8 | * published by the Free Software Foundation. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #ifndef __CTRMODE_H__ 21 | #define __CTRMODE_H__ 22 | 23 | /* Encrypt or decrypt data in AES-CTR mode. (The operations are the 24 | * same.) We roll our own here just to double-check that the calls 25 | * libotr makes to libgcrypt are doing the right thing. */ 26 | void aes_ctr_crypt(unsigned char *out, const unsigned char *in, size_t len, 27 | unsigned char key[16], unsigned char ctrtop[8]); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /toolkit/otr_mackey.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging Toolkit 3 | * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov 4 | * 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of version 2 of the GNU General Public License as 8 | * published by the Free Software Foundation. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | /* system headers */ 21 | #include 22 | #include 23 | 24 | /* toolkit headers */ 25 | #include "parse.h" 26 | #include "sesskeys.h" 27 | 28 | static void usage(const char *progname) 29 | { 30 | fprintf(stderr, "Usage: %s aeskey\n" 31 | "Calculate and display the MAC key derived from a given AES key.\n", 32 | progname); 33 | exit(1); 34 | } 35 | 36 | int main(int argc, char **argv) 37 | { 38 | unsigned char *argbuf; 39 | size_t argbuflen; 40 | unsigned char mackey[20]; 41 | 42 | if (argc != 2) { 43 | usage(argv[0]); 44 | } 45 | 46 | argv_to_buf(&argbuf, &argbuflen, argv[1]); 47 | /* AES keys are 128 bits long, so check for that */ 48 | if (!argbuf) { 49 | usage(argv[0]); 50 | } 51 | 52 | if (argbuflen != 16) { 53 | fprintf(stderr, "The AES key must be 32 hex chars long.\n"); 54 | usage(argv[0]); 55 | } 56 | 57 | sesskeys_make_mac(mackey, argbuf); 58 | 59 | dump_data(stdout, "AES key", argbuf, 16); 60 | dump_data(stdout, "MAC key", mackey, 20); 61 | 62 | free(argbuf); 63 | fflush(stdout); 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /toolkit/otr_modify.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging Toolkit 3 | * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov 4 | * 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of version 2 of the GNU General Public License as 8 | * published by the Free Software Foundation. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | /* system headers */ 21 | #include 22 | #include 23 | 24 | /* libotr headers */ 25 | #include "proto.h" 26 | 27 | /* toolkit headers */ 28 | #include "readotr.h" 29 | #include "parse.h" 30 | #include "sha1hmac.h" 31 | 32 | static void usage(const char *progname) 33 | { 34 | fprintf(stderr, "Usage: %s mackey old_text new_text offset\n" 35 | "Read an OTR Data Message from stdin. Even if we can't read the\n" 36 | "data because we don't know either the AES key or the DH privkey,\n" 37 | "but we can make a good guess that the substring \"old_text\"\n" 38 | "appears at the given offset in the message, replace the old_text\n" 39 | "with the new_text (which must be of the same length), recalculate\n" 40 | "the MAC with the given mackey, and output the resulting Data message.\n", 41 | progname); 42 | exit(1); 43 | } 44 | 45 | int main(int argc, char **argv) 46 | { 47 | unsigned char *mackey; 48 | size_t mackeylen; 49 | unsigned char macval[20]; 50 | char *otrmsg = NULL; 51 | DataMsg datamsg; 52 | size_t textlen; 53 | unsigned int offset; 54 | const unsigned char *old_text, *new_text; 55 | char *newdatamsg; 56 | size_t i; 57 | 58 | if (argc != 5) { 59 | usage(argv[0]); 60 | } 61 | 62 | argv_to_buf(&mackey, &mackeylen, argv[1]); 63 | if (!mackey) { 64 | usage(argv[0]); 65 | } 66 | 67 | if (mackeylen != 20) { 68 | fprintf(stderr, "The MAC key must be 40 hex chars long.\n"); 69 | usage(argv[0]); 70 | } 71 | 72 | textlen = strlen(argv[2]); 73 | if (textlen != strlen(argv[3])) { 74 | fprintf(stderr, "The old_text and new_text must be of the same " 75 | "length.\n"); 76 | usage(argv[0]); 77 | } 78 | old_text = (const unsigned char *)argv[2]; 79 | new_text = (const unsigned char *)argv[3]; 80 | 81 | if (sscanf(argv[4], "%u", &offset) != 1) { 82 | fprintf(stderr, "Unparseable offset given.\n"); 83 | usage(argv[0]); 84 | } 85 | 86 | otrmsg = readotr(stdin); 87 | if (otrmsg == NULL) { 88 | fprintf(stderr, "No OTR Data Message found on stdin.\n"); 89 | exit(1); 90 | } 91 | 92 | if (otrl_proto_message_type(otrmsg) != OTRL_MSGTYPE_DATA) { 93 | fprintf(stderr, "OTR Non-Data Message found on stdin.\n"); 94 | exit(1); 95 | } 96 | 97 | datamsg = parse_datamsg(otrmsg); 98 | free(otrmsg); 99 | if (datamsg == NULL) { 100 | fprintf(stderr, "Invalid OTR Data Message found on stdin.\n"); 101 | exit(1); 102 | } 103 | 104 | /* Check the MAC */ 105 | sha1hmac(macval, mackey, datamsg->macstart, 106 | datamsg->macend - datamsg->macstart); 107 | if (memcmp(macval, datamsg->mac, 20)) { 108 | fprintf(stderr, "MAC does not verify: wrong MAC key?\n"); 109 | exit(1); 110 | } 111 | 112 | /* Modify the ciphertext */ 113 | for(i=0; iencmsglen; ++i) { 114 | datamsg->encmsg[offset+i] ^= (old_text[i] ^ new_text[i]); 115 | } 116 | 117 | /* Recalculate the MAC */ 118 | newdatamsg = remac_datamsg(datamsg, mackey); 119 | printf("%s\n", newdatamsg); 120 | free(newdatamsg); 121 | 122 | free_datamsg(datamsg); 123 | free(mackey); 124 | fflush(stdout); 125 | return 0; 126 | } 127 | -------------------------------------------------------------------------------- /toolkit/otr_parse.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging Toolkit 3 | * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander, 4 | * Nikita Borisov 5 | * 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of version 2 of the GNU General Public License as 9 | * published by the Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | /* system headers */ 22 | #include 23 | #include 24 | 25 | /* libotr headers */ 26 | #include "proto.h" 27 | 28 | /* toolkit headers */ 29 | #include "readotr.h" 30 | #include "parse.h" 31 | 32 | static void parse(const char *msg) 33 | { 34 | OtrlMessageType mtype = otrl_proto_message_type(msg); 35 | CommitMsg cmsg; 36 | KeyMsg kmsg; 37 | RevealSigMsg rmsg; 38 | SignatureMsg smsg; 39 | KeyExchMsg keyexch; 40 | DataMsg datamsg; 41 | 42 | switch(mtype) { 43 | case OTRL_MSGTYPE_QUERY: 44 | printf("OTR Query:\n\t%s\n\n", msg); 45 | break; 46 | case OTRL_MSGTYPE_DH_COMMIT: 47 | cmsg = parse_commit(msg); 48 | if (!cmsg) { 49 | printf("Invalid D-H Commit Message\n\n"); 50 | break; 51 | } 52 | 53 | printf("D-H Commit Message:\n"); 54 | 55 | dump_data(stdout, "\tVersion", &(cmsg->version), 1); 56 | if (cmsg->version == 3) { 57 | dump_int(stdout, "\tSender instance", cmsg->sender_instance); 58 | dump_int(stdout, "\tReceiver instance", 59 | cmsg->receiver_instance); 60 | } 61 | dump_data(stdout, "\tEncrypted Key", cmsg->enckey, 62 | cmsg->enckeylen); 63 | dump_data(stdout, "\tHashed Key", cmsg->hashkey, 64 | cmsg->hashkeylen); 65 | printf("\n"); 66 | free_commit(cmsg); 67 | break; 68 | case OTRL_MSGTYPE_DH_KEY: 69 | kmsg = parse_key(msg); 70 | if (!kmsg) { 71 | printf("Invalid D-H Key Message\n\n"); 72 | break; 73 | } 74 | printf("D-H Key Message:\n"); 75 | dump_data(stdout, "\tVersion", &(kmsg->version), 1); 76 | if (kmsg->version == 3) { 77 | dump_int(stdout, "\tSender instance", kmsg->sender_instance); 78 | dump_int(stdout, "\tReceiver instance", 79 | kmsg->receiver_instance); 80 | } 81 | dump_mpi(stdout, "\tD-H Key", kmsg->y); 82 | printf("\n"); 83 | free_key(kmsg); 84 | break; 85 | case OTRL_MSGTYPE_REVEALSIG: 86 | rmsg = parse_revealsig(msg); 87 | if (!rmsg) { 88 | printf("Invalid Reveal Signature Message\n\n"); 89 | break; 90 | } 91 | printf("Reveal Signature Message:\n"); 92 | dump_data(stdout, "\tVersion", &(rmsg->version), 1); 93 | if (rmsg->version == 3) { 94 | dump_int(stdout, "\tSender instance", rmsg->sender_instance); 95 | dump_int(stdout, "\tReceiver instance", 96 | rmsg->receiver_instance); 97 | } 98 | dump_data(stdout, "\tKey", rmsg->key, rmsg->keylen); 99 | dump_data(stdout, "\tEncrypted Signature", 100 | rmsg->encsig, rmsg->encsiglen); 101 | dump_data(stdout, "\tMAC", rmsg->mac, 20); 102 | printf("\n"); 103 | free_revealsig(rmsg); 104 | break; 105 | case OTRL_MSGTYPE_SIGNATURE: 106 | smsg = parse_signature(msg); 107 | if (!smsg) { 108 | printf("Invalid Signature Message\n\n"); 109 | break; 110 | } 111 | printf("Signature Message:\n"); 112 | dump_data(stdout, "\tVersion", &(smsg->version), 1); 113 | if (smsg->version == 3) { 114 | dump_int(stdout, "\tSender instance", smsg->sender_instance); 115 | dump_int(stdout, "\tReceiver instance", 116 | smsg->receiver_instance); 117 | } 118 | dump_data(stdout, "\tEncrypted Signature", 119 | smsg->encsig, smsg->encsiglen); 120 | dump_data(stdout, "\tMAC", smsg->mac, 20); 121 | printf("\n"); 122 | free_signature(smsg); 123 | break; 124 | case OTRL_MSGTYPE_V1_KEYEXCH: 125 | keyexch = parse_keyexch(msg); 126 | if (!keyexch) { 127 | printf("Invalid Key Exchange Message\n\n"); 128 | break; 129 | } 130 | printf("Key Exchange Message:\n"); 131 | dump_int(stdout, "\tReply", keyexch->reply); 132 | dump_mpi(stdout, "\tDSA p", keyexch->p); 133 | dump_mpi(stdout, "\tDSA q", keyexch->q); 134 | dump_mpi(stdout, "\tDSA g", keyexch->g); 135 | dump_mpi(stdout, "\tDSA e", keyexch->e); 136 | dump_int(stdout, "\tKeyID", keyexch->keyid); 137 | dump_mpi(stdout, "\tDH y", keyexch->y); 138 | dump_mpi(stdout, "\tSIG r", keyexch->r); 139 | dump_mpi(stdout, "\tSIG s", keyexch->s); 140 | printf("\n"); 141 | free_keyexch(keyexch); 142 | break; 143 | case OTRL_MSGTYPE_DATA: 144 | datamsg = parse_datamsg(msg); 145 | if (!datamsg) { 146 | printf("Invalid Data Message\n\n"); 147 | break; 148 | } 149 | printf("Data Message:\n"); 150 | 151 | dump_data(stdout, "\tVersion", &(datamsg->version), 1); 152 | if (datamsg->flags >= 0) { 153 | dump_int(stdout, "\tFlags", datamsg->flags); 154 | } 155 | 156 | if (datamsg->version == 3) { 157 | dump_int(stdout, "\tSender instance", datamsg->sender_instance); 158 | dump_int(stdout, "\tReceiver instance", 159 | datamsg->receiver_instance); 160 | } 161 | 162 | dump_int(stdout, "\tSender keyid", datamsg->sender_keyid); 163 | dump_int(stdout, "\tRcpt keyid", datamsg->rcpt_keyid); 164 | dump_mpi(stdout, "\tDH y", datamsg->y); 165 | dump_data(stdout, "\tCounter", datamsg->ctr, 8); 166 | dump_data(stdout, "\tEncrypted message", datamsg->encmsg, 167 | datamsg->encmsglen); 168 | dump_data(stdout, "\tMAC", datamsg->mac, 20); 169 | if (datamsg->mackeyslen > 0) { 170 | size_t len = datamsg->mackeyslen; 171 | unsigned char *mks = datamsg->mackeys; 172 | unsigned int i = 0; 173 | printf("\tRevealed MAC keys:\n"); 174 | 175 | while(len > 19) { 176 | char title[20]; 177 | sprintf(title, "\t\tKey %u", ++i); 178 | dump_data(stdout, title, mks, 20); 179 | mks += 20; len -= 20; 180 | } 181 | } 182 | 183 | printf("\n"); 184 | free_datamsg(datamsg); 185 | break; 186 | case OTRL_MSGTYPE_ERROR: 187 | printf("OTR Error:\n\t%s\n\n", msg); 188 | break; 189 | case OTRL_MSGTYPE_TAGGEDPLAINTEXT: 190 | printf("Tagged plaintext message:\n\t%s\n\n", msg); 191 | break; 192 | case OTRL_MSGTYPE_NOTOTR: 193 | printf("Not an OTR message:\n\t%s\n\n", msg); 194 | break; 195 | case OTRL_MSGTYPE_UNKNOWN: 196 | printf("Unrecognized OTR message:\n\t%s\n\n", msg); 197 | break; 198 | } 199 | fflush(stdout); 200 | } 201 | 202 | static void usage(const char *progname) 203 | { 204 | fprintf(stderr, "Usage: %s\n" 205 | "Read Off-the-Record (OTR) Key Exchange and/or Data messages from stdin\n" 206 | "and display their contents in a more readable format.\n", progname); 207 | exit(1); 208 | } 209 | 210 | int main(int argc, char **argv) 211 | { 212 | char *otrmsg = NULL; 213 | 214 | if (argc != 1) { 215 | usage(argv[0]); 216 | } 217 | 218 | while ((otrmsg = readotr(stdin)) != NULL) { 219 | parse(otrmsg); 220 | free(otrmsg); 221 | } 222 | 223 | return 0; 224 | } 225 | -------------------------------------------------------------------------------- /toolkit/otr_readforge.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging Toolkit 3 | * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov 4 | * 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of version 2 of the GNU General Public License as 8 | * published by the Free Software Foundation. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | /* system headers */ 21 | #include 22 | #include 23 | 24 | /* libotr headers */ 25 | #include "proto.h" 26 | 27 | /* toolkit headers */ 28 | #include "readotr.h" 29 | #include "parse.h" 30 | #include "sesskeys.h" 31 | #include "sha1hmac.h" 32 | #include "ctrmode.h" 33 | 34 | static void usage(const char *progname) 35 | { 36 | fprintf(stderr, "Usage: %s aeskey [new_message]\n" 37 | "Read an OTR Data Message from stdin. Use the given AES key to\n" 38 | "verify its MAC and decrypt the message to stdout. If new_message\n" 39 | "is given, output a new OTR Data Message with the same fields as the\n" 40 | "original, but with the message replaced by new_message\n", progname); 41 | exit(1); 42 | } 43 | 44 | int main(int argc, char **argv) 45 | { 46 | unsigned char *aeskey; 47 | unsigned char mackey[20]; 48 | unsigned char macval[20]; 49 | size_t aeskeylen; 50 | unsigned char *plaintext, *ciphertext; 51 | char *otrmsg = NULL; 52 | DataMsg datamsg; 53 | 54 | if (argc != 2 && argc != 3) { 55 | usage(argv[0]); 56 | } 57 | 58 | argv_to_buf(&aeskey, &aeskeylen, argv[1]); 59 | if (!aeskey) { 60 | usage(argv[0]); 61 | } 62 | 63 | if (aeskeylen != 16) { 64 | fprintf(stderr, "The AES key must be 32 hex chars long.\n"); 65 | usage(argv[0]); 66 | } 67 | 68 | otrmsg = readotr(stdin); 69 | if (otrmsg == NULL) { 70 | fprintf(stderr, "No OTR Data Message found on stdin.\n"); 71 | exit(1); 72 | } 73 | 74 | if (otrl_proto_message_type(otrmsg) != OTRL_MSGTYPE_DATA) { 75 | fprintf(stderr, "OTR Non-Data Message found on stdin.\n"); 76 | exit(1); 77 | } 78 | 79 | datamsg = parse_datamsg(otrmsg); 80 | free(otrmsg); 81 | if (datamsg == NULL) { 82 | fprintf(stderr, "Invalid OTR Data Message found on stdin.\n"); 83 | exit(1); 84 | } 85 | 86 | /* Create the MAC key */ 87 | sesskeys_make_mac(mackey, aeskey); 88 | 89 | /* Check the MAC */ 90 | sha1hmac(macval, mackey, datamsg->macstart, 91 | datamsg->macend - datamsg->macstart); 92 | if (memcmp(macval, datamsg->mac, 20)) { 93 | fprintf(stderr, "MAC does not verify: wrong AES key?\n"); 94 | } else { 95 | /* Decrypt the message */ 96 | plaintext = malloc(datamsg->encmsglen+1); 97 | if (!plaintext) { 98 | fprintf(stderr, "Out of memory!\n"); 99 | exit(1); 100 | } 101 | aes_ctr_crypt(plaintext, datamsg->encmsg, datamsg->encmsglen, 102 | aeskey, datamsg->ctr); 103 | plaintext[datamsg->encmsglen] = '\0'; 104 | printf("Plaintext: ``%s''\n", plaintext); 105 | free(plaintext); 106 | } 107 | 108 | /* Do we want to forge a message? */ 109 | if (argv[2] != NULL) { 110 | char *newdatamsg; 111 | size_t newlen = strlen(argv[2]); 112 | ciphertext = malloc(newlen); 113 | if (!ciphertext && newlen > 0) { 114 | fprintf(stderr, "Out of memory!\n"); 115 | exit(1); 116 | } 117 | aes_ctr_crypt(ciphertext, (const unsigned char *)argv[2], newlen, 118 | aeskey, datamsg->ctr); 119 | free(datamsg->encmsg); 120 | datamsg->encmsg = ciphertext; 121 | datamsg->encmsglen = newlen; 122 | 123 | newdatamsg = remac_datamsg(datamsg, mackey); 124 | 125 | printf("%s\n", newdatamsg); 126 | free(newdatamsg); 127 | } 128 | 129 | free_datamsg(datamsg); 130 | free(aeskey); 131 | fflush(stdout); 132 | return 0; 133 | } 134 | -------------------------------------------------------------------------------- /toolkit/otr_remac.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging Toolkit 3 | * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander, 4 | * Nikita Borisov 5 | * 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of version 2 of the GNU General Public License as 9 | * published by the Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | /* system headers */ 22 | #include 23 | #include 24 | 25 | /* libgcrypt headers */ 26 | #include 27 | 28 | /* toolkit headers */ 29 | #include "parse.h" 30 | #include "sha1hmac.h" 31 | 32 | static void usage(const char *progname) 33 | { 34 | fprintf(stderr, "Usage: %s mackey sender_instance receiver_instance " 35 | "flags snd_keyid rcp_keyid pubkey counter encdata revealed_mackeys\n" 36 | "Make a new Data message, with the given pieces (note that the\n" 37 | "data part is already encrypted). MAC it with the given mackey.\n" 38 | "mackey, pubkey, counter, encdata, and revealed_mackeys are given\n" 39 | "as strings of hex chars. snd_keyid and rcp_keyid are decimal integers.\n", 40 | progname); 41 | exit(1); 42 | } 43 | 44 | int main(int argc, char **argv) 45 | { 46 | unsigned char *mackey; 47 | size_t mackeylen; 48 | unsigned int snd_keyid, rcp_keyid; 49 | int flags; 50 | unsigned char version = 3; 51 | unsigned int sender_instance; 52 | unsigned int receiver_instance; 53 | unsigned char *pubkey; 54 | size_t pubkeylen; 55 | gcry_mpi_t pubv; 56 | unsigned char *ctr; 57 | size_t ctrlen; 58 | unsigned char *encdata; 59 | size_t encdatalen; 60 | unsigned char *mackeys; 61 | size_t mackeyslen; 62 | char *newdatamsg; 63 | 64 | if (argc != 11) { 65 | usage(argv[0]); 66 | } 67 | 68 | argv_to_buf(&mackey, &mackeylen, argv[1]); 69 | if (!mackey) { 70 | usage(argv[0]); 71 | } 72 | 73 | if (mackeylen != 20) { 74 | fprintf(stderr, "The MAC key must be 40 hex chars long.\n"); 75 | usage(argv[0]); 76 | } 77 | 78 | if (sscanf(argv[2], "%u", &sender_instance) != 1) { 79 | fprintf(stderr, "Unparseable sender_instance given.\n"); 80 | usage(argv[0]); 81 | } 82 | 83 | if (sscanf(argv[3], "%u", &receiver_instance) != 1) { 84 | fprintf(stderr, "Unparseable receiver_instance given.\n"); 85 | usage(argv[0]); 86 | } 87 | 88 | if (sscanf(argv[4], "%d", &flags) != 1) { 89 | fprintf(stderr, "Unparseable flags given.\n"); 90 | usage(argv[0]); 91 | } 92 | 93 | if (sscanf(argv[5], "%u", &snd_keyid) != 1) { 94 | fprintf(stderr, "Unparseable snd_keyid given.\n"); 95 | usage(argv[0]); 96 | } 97 | 98 | if (sscanf(argv[6], "%u", &rcp_keyid) != 1) { 99 | fprintf(stderr, "Unparseable rcp_keyid given.\n"); 100 | usage(argv[0]); 101 | } 102 | 103 | argv_to_buf(&pubkey, &pubkeylen, argv[7]); 104 | if (!pubkey) { 105 | usage(argv[0]); 106 | } 107 | gcry_mpi_scan(&pubv, GCRYMPI_FMT_USG, pubkey, pubkeylen, NULL); 108 | free(pubkey); 109 | 110 | argv_to_buf(&ctr, &ctrlen, argv[8]); 111 | if (!ctr) { 112 | usage(argv[0]); 113 | } 114 | 115 | if (ctrlen != 8) { 116 | fprintf(stderr, "The counter must be 16 hex chars long.\n"); 117 | usage(argv[0]); 118 | } 119 | 120 | argv_to_buf(&encdata, &encdatalen, argv[9]); 121 | if (!encdata) { 122 | usage(argv[0]); 123 | } 124 | 125 | argv_to_buf(&mackeys, &mackeyslen, argv[10]); 126 | if (!mackeys) { 127 | usage(argv[0]); 128 | } 129 | 130 | newdatamsg = assemble_datamsg(mackey, version, sender_instance, 131 | receiver_instance, flags, snd_keyid, rcp_keyid, pubv, ctr, encdata, 132 | encdatalen, mackeys, mackeyslen); 133 | printf("%s\n", newdatamsg); 134 | free(newdatamsg); 135 | 136 | free(mackey); 137 | gcry_mpi_release(pubv); 138 | free(ctr); 139 | free(encdata); 140 | free(mackeys); 141 | fflush(stdout); 142 | return 0; 143 | } 144 | -------------------------------------------------------------------------------- /toolkit/otr_sesskeys.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging Toolkit 3 | * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov 4 | * 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of version 2 of the GNU General Public License as 8 | * published by the Free Software Foundation. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | /* system headers */ 21 | #include 22 | #include 23 | 24 | /* toolkit headers */ 25 | #include "parse.h" 26 | #include "sesskeys.h" 27 | 28 | static void usage(const char *progname) 29 | { 30 | fprintf(stderr, "Usage: %s our_privkey their_pubkey\n" 31 | "Calculate and display our public key, the session id, two AES keys,\n" 32 | "and two MAC keys generated by the given DH private key and public key.\n", 33 | progname); 34 | exit(1); 35 | } 36 | 37 | int main(int argc, char **argv) 38 | { 39 | unsigned char *argbuf; 40 | size_t argbuflen; 41 | gcry_mpi_t our_x, our_y, their_y; 42 | unsigned char *pubbuf; 43 | size_t publen; 44 | unsigned char sessionid[20], sendenc[16], rcvenc[16]; 45 | unsigned char sendmac[20], rcvmac[20]; 46 | int is_high; 47 | 48 | if (argc != 3) { 49 | usage(argv[0]); 50 | } 51 | 52 | argv_to_buf(&argbuf, &argbuflen, argv[1]); 53 | /* Private keys are only 320 bits long, so check for that to make 54 | * sure they didn't get the args the wrong way around */ 55 | if (!argbuf || argbuflen > 40) usage(argv[0]); 56 | gcry_mpi_scan(&our_x, GCRYMPI_FMT_USG, argbuf, argbuflen, NULL); 57 | free(argbuf); 58 | argv_to_buf(&argbuf, &argbuflen, argv[2]); 59 | if (!argbuf) usage(argv[0]); 60 | gcry_mpi_scan(&their_y, GCRYMPI_FMT_USG, argbuf, argbuflen, NULL); 61 | free(argbuf); 62 | 63 | sesskeys_gen(sessionid, sendenc, rcvenc, &is_high, &our_y, our_x, their_y); 64 | sesskeys_make_mac(sendmac, sendenc); 65 | sesskeys_make_mac(rcvmac, rcvenc); 66 | 67 | /* Print our public key into a buffer */ 68 | gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &publen, our_y); 69 | pubbuf = malloc(publen); 70 | if (!pubbuf) { 71 | fprintf(stderr, "Out of memory!\n"); 72 | exit(1); 73 | } 74 | gcry_mpi_print(GCRYMPI_FMT_USG, pubbuf, publen, NULL, our_y); 75 | 76 | puts(""); 77 | printf("We are the %s end of this key exchange.\n", 78 | is_high ? "high" : "low"); 79 | puts(""); 80 | dump_data(stdout, "Our public key", pubbuf, publen); 81 | puts(""); 82 | dump_data(stdout, "Session id", sessionid, 20); 83 | puts(""); 84 | dump_data(stdout, "Sending AES key", sendenc, 16); 85 | dump_data(stdout, "Sending MAC key", sendmac, 20); 86 | dump_data(stdout, "Receiving AES key", rcvenc, 16); 87 | dump_data(stdout, "Receiving MAC key", rcvmac, 20); 88 | puts(""); 89 | fflush(stdout); 90 | 91 | return 0; 92 | } 93 | -------------------------------------------------------------------------------- /toolkit/otr_toolkit.1: -------------------------------------------------------------------------------- 1 | .\" Hey, EMACS: -*- nroff -*- 2 | .\" First parameter, NAME, should be all caps 3 | .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection 4 | .\" other parameters are allowed: see man(7), man(1) 5 | .TH OTR_PARSE 1 "March 14, 2012" 6 | .\" Please adjust this date whenever revising the manpage. 7 | .\" 8 | .\" Some roff macros, for reference: 9 | .\" .nh disable hyphenation 10 | .\" .hy enable hyphenation 11 | .\" .ad l left justify 12 | .\" .ad b justify to both left and right margins 13 | .\" .nf disable filling 14 | .\" .fi enable filling 15 | .\" .br insert line break 16 | .\" .sp insert n+1 empty lines 17 | .\" for manpage-specific macros, see man(7) 18 | .SH NAME 19 | otr_parse, otr_sesskeys, otr_mackey, otr_readforge, otr_modify, otr_remac \- Process Off-the-Record Messaging transcripts 20 | .SH SYNOPSIS 21 | .B otr_parse 22 | .br 23 | .B otr_sesskeys 24 | .I our_privkey their_pubkey 25 | .br 26 | .B otr_mackey 27 | .I aes_enc_key 28 | .br 29 | .B otr_readforge 30 | .I aes_enc_key [newmsg] 31 | .br 32 | .B otr_modify 33 | .I mackey old_text new_text offset 34 | .br 35 | .B otr_remac 36 | .I mackey sender_instance receiver_instance flags snd_keyid rcv_keyid pubkey counter encdata revealed_mackeys 37 | .SH DESCRIPTION 38 | Off-the-Record (OTR) Messaging allows you to have private conversations 39 | over IM by providing: 40 | - Encryption 41 | - No one else can read your instant messages. 42 | - Authentication 43 | - You are assured the correspondent is who you think it is. 44 | - Deniability 45 | - The messages you send do \fInot\fP have digital signatures that are 46 | checkable by a third party. Anyone can forge messages after a 47 | conversation to make them look like they came from you. However, 48 | \fIduring\fP a conversation, your correspondent is assured the messages 49 | he sees are authentic and unmodified. 50 | - Perfect forward secrecy 51 | - If you lose control of your private keys, no previous conversation 52 | is compromised. 53 | .PP 54 | The OTR Toolkit is useful for analyzing and/or 55 | forging OTR messages. Why do we offer this? Primarily, to make 56 | absolutely sure that transcripts of OTR conversations are really easy 57 | to forge after the fact. [Note that \fIduring\fP an OTR conversation, 58 | messages can't be forged without real-time access to the secret keys on 59 | the participants' computers, and in that case, all security has already 60 | been lost.] Easily-forgeable transcripts help us provide the 61 | "Deniability" property: if someone claims you said something over OTR, 62 | they'll have no proof, as anyone at all can modify a transcript to make 63 | it say whatever they like, and still have all the verification come out 64 | correctly. 65 | 66 | Here are the six programs in the toolkit: 67 | 68 | - otr_parse 69 | - Parse OTR messages given on stdin, showing the values of all the 70 | fields in OTR protocol messages. 71 | 72 | - otr_sesskeys our_privkey their_pubkey 73 | - Shows our public key, the session id, two AES and two MAC keys 74 | derived from the given Diffie-Hellman keys (one private, one public). 75 | 76 | - otr_mackey aes_enc_key 77 | - Shows the MAC key derived from the given AES key. 78 | 79 | - otr_readforge aes_enc_key [newmsg] 80 | - Decrypts an OTR Data message using the given AES key, and displays 81 | the message. 82 | - If newmsg is given, replace the message with that one, encrypt 83 | and MAC it properly, and output the resulting OTR Data Message. 84 | This works even if the given key was not correct for the original 85 | message, so as to enable complete forgeries. 86 | 87 | - otr_modify mackey old_text new_text offset 88 | - Even if you can't read the data because you don't know either 89 | the AES key or the Diffie-Hellman private key, but you can make a 90 | good guess that the substring "old_text" appears at the given 91 | offset in the message, replace the old_text with the new_text 92 | (which must be of the same length), recalculate the MAC with the 93 | given mackey, and output the resulting Data message. 94 | - Note that, even if you don't know any text in an existing message, 95 | you can still forge messages of your choice using the 96 | otr_readforge command, above. 97 | 98 | - otr_remac mackey sender_instance receiver_instance flags snd_keyid rcv_keyid pubkey counter encdata revealed_mackeys 99 | - Make a new OTR protocol version 3 Data Message, with the given 100 | pieces (note that the data part is already encrypted). MAC it 101 | with the given mackey. 102 | 103 | .SH SEE ALSO 104 | .BR "Off-the-Record Messaging" , 105 | at 106 | .UR https://www.cypherpunks.ca/otr/ 107 | https://www.cypherpunks.ca/otr/ 108 | .UE 109 | .SH AUTHOR 110 | otr_toolkit was written by the OTR Dev Team . 111 | -------------------------------------------------------------------------------- /toolkit/parse.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging Toolkit 3 | * Copyright (C) 2004-2012 Ian Goldberg, Rob Smits, Chris Alexander, 4 | * Nikita Borisov 5 | * 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of version 2 of the GNU General Public License as 9 | * published by the Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #ifndef __PARSE_H__ 22 | #define __PARSE_H__ 23 | 24 | #include 25 | 26 | typedef struct s_KeyExchMsg { 27 | unsigned char *raw; /* The base64-decoded data; must be free()d */ 28 | unsigned char reply; 29 | gcry_mpi_t p, q, g, e; 30 | unsigned int keyid; 31 | gcry_mpi_t y; 32 | gcry_mpi_t r, s; 33 | unsigned char *sigstart; /* Pointers into the "raw" array. Don't */ 34 | unsigned char *sigend; /* free() these. */ 35 | } * KeyExchMsg; 36 | 37 | typedef struct s_DataMsg { 38 | unsigned char *raw; /* The base64-decoded data; must be free()d */ 39 | size_t rawlen; 40 | int flags; 41 | unsigned char version; 42 | unsigned int sender_instance; 43 | unsigned int receiver_instance; 44 | unsigned int sender_keyid; 45 | unsigned int rcpt_keyid; 46 | gcry_mpi_t y; 47 | unsigned char ctr[8]; 48 | unsigned char *encmsg; /* A copy; must be free()d */ 49 | size_t encmsglen; 50 | unsigned char mac[20]; 51 | unsigned char *mackeys; /* A copy; must be free()d */ 52 | size_t mackeyslen; 53 | unsigned char *macstart; /* Pointers into the "raw" array. Don't */ 54 | unsigned char *macend; /* free() these. */ 55 | } * DataMsg; 56 | 57 | typedef struct s_CommitMsg { 58 | unsigned char *raw; /* The base64-decoded data; must be free()d */ 59 | unsigned char version; 60 | unsigned int sender_instance; 61 | unsigned int receiver_instance; 62 | unsigned char *enckey; 63 | size_t enckeylen; 64 | unsigned char *hashkey; 65 | size_t hashkeylen; 66 | } * CommitMsg; 67 | 68 | typedef struct s_KeyMsg { 69 | unsigned char *raw; /* The base64-decoded data; must be free()d */ 70 | unsigned char version; 71 | unsigned int sender_instance; 72 | unsigned int receiver_instance; 73 | gcry_mpi_t y; 74 | } * KeyMsg; 75 | 76 | typedef struct s_RevealSigMsg { 77 | unsigned char *raw; /* The base64-decoded data; must be free()d */ 78 | unsigned char version; 79 | unsigned int sender_instance; 80 | unsigned int receiver_instance; 81 | unsigned char *key; 82 | size_t keylen; 83 | unsigned char *encsig; 84 | size_t encsiglen; 85 | unsigned char mac[20]; 86 | } * RevealSigMsg; 87 | 88 | typedef struct s_SignatureMsg { 89 | unsigned char *raw; /* The base64-decoded data; must be free()d */ 90 | unsigned char version; 91 | unsigned int sender_instance; 92 | unsigned int receiver_instance; 93 | unsigned char *encsig; 94 | size_t encsiglen; 95 | unsigned char mac[20]; 96 | } * SignatureMsg; 97 | 98 | /* Dump an unsigned int to a FILE * */ 99 | void dump_int(FILE *stream, const char *title, unsigned int val); 100 | 101 | /* Dump an mpi to a FILE * */ 102 | void dump_mpi(FILE *stream, const char *title, gcry_mpi_t val); 103 | 104 | /* Dump data to a FILE * */ 105 | void dump_data(FILE *stream, const char *title, const unsigned char *data, 106 | size_t datalen); 107 | 108 | /* Parse a Key Exchange Message into a newly-allocated KeyExchMsg structure */ 109 | KeyExchMsg parse_keyexch(const char *msg); 110 | 111 | /* Deallocate a KeyExchMsg and all of the data it points to */ 112 | void free_keyexch(KeyExchMsg keyexch); 113 | 114 | /* Parse a D-H Commit Message into a newly-allocated CommitMsg structure */ 115 | CommitMsg parse_commit(const char *msg); 116 | 117 | /* Parse a Data Message into a newly-allocated DataMsg structure */ 118 | DataMsg parse_datamsg(const char *msg); 119 | 120 | /* Deallocate a CommitMsg and all of the data it points to */ 121 | void free_commit(CommitMsg cmsg); 122 | 123 | /* Parse a Reveal Signature Message into a newly-allocated RevealSigMsg 124 | * structure */ 125 | RevealSigMsg parse_revealsig(const char *msg); 126 | 127 | /* Deallocate a RevealSigMsg and all of the data it points to */ 128 | void free_revealsig(RevealSigMsg rmsg); 129 | 130 | /* Parse a Signature Message into a newly-allocated SignatureMsg structure */ 131 | SignatureMsg parse_signature(const char *msg); 132 | 133 | /* Deallocate a SignatureMsg and all of the data it points to */ 134 | void free_signature(SignatureMsg smsg); 135 | 136 | /* Parse a D-H Key Message into a newly-allocated KeyMsg structure */ 137 | KeyMsg parse_key(const char *msg); 138 | 139 | /* Deallocate a KeyMsg and all of the data it points to */ 140 | void free_key(KeyMsg cmsg); 141 | 142 | /* Recalculate the MAC on the message, base64-encode the resulting MAC'd 143 | * message, and put on the appropriate header and footer. Return a 144 | * newly-allocated pointer to the result, which the caller will have to 145 | * free(). */ 146 | char *remac_datamsg(DataMsg datamsg, unsigned char mackey[20]); 147 | 148 | /* Assemble a new Data Message from its pieces. Return a 149 | * newly-allocated string containing the base64 representation. */ 150 | char *assemble_datamsg(unsigned char mackey[20], 151 | unsigned char version, unsigned int sender_instance, 152 | unsigned int receiver_instance, int flags, unsigned int sender_keyid, 153 | unsigned int rcpt_keyid, gcry_mpi_t y, 154 | unsigned char ctr[8], unsigned char *encmsg, size_t encmsglen, 155 | unsigned char *mackeys, size_t mackeyslen); 156 | 157 | /* Deallocate a DataMsg and all of the data it points to */ 158 | void free_datamsg(DataMsg datamsg); 159 | 160 | /* Convert a string of hex chars to a buffer of unsigned chars. */ 161 | void argv_to_buf(unsigned char **bufp, size_t *lenp, char *arg); 162 | 163 | #endif 164 | -------------------------------------------------------------------------------- /toolkit/readotr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging Toolkit 3 | * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov 4 | * 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of version 2 of the GNU General Public License as 8 | * published by the Free Software Foundation. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | /* system headers */ 21 | #include 22 | #include 23 | #include 24 | 25 | typedef struct { 26 | char *data; 27 | size_t len; 28 | size_t alloclen; 29 | } Buffer; 30 | 31 | static void buf_new(Buffer *bufp) 32 | { 33 | bufp->data = NULL; 34 | bufp->len = 0; 35 | bufp->alloclen = 0; 36 | } 37 | 38 | static void buf_put(Buffer *bufp, const char *str, size_t len) 39 | { 40 | while (bufp->len + len + 1 > bufp->alloclen) { 41 | char *newdata = realloc(bufp->data, bufp->alloclen + 1024); 42 | if (!newdata) { 43 | fprintf(stderr, "Out of memory!\n"); 44 | exit(1); 45 | } 46 | bufp->data = newdata; 47 | bufp->alloclen += 1024; 48 | } 49 | memmove(bufp->data + bufp->len, str, len); 50 | bufp->len += len; 51 | bufp->data[bufp->len] = '\0'; 52 | } 53 | 54 | static void buf_putc(Buffer *bufp, char c) 55 | { 56 | buf_put(bufp, &c, 1); 57 | } 58 | 59 | /* Read from the given stream until we see a complete OTR Key Exchange 60 | * or OTR Data message. Return a newly-allocated pointer to a copy of 61 | * this message, which the caller should free(). Returns NULL if no 62 | * such message could be found. */ 63 | char *readotr(FILE *stream) 64 | { 65 | int seen = 0; 66 | const char header[] = "?OTR:"; /* There are no '?' chars other than 67 | the leading one */ 68 | int headerlen = strlen(header); 69 | Buffer buf; 70 | 71 | while(seen < headerlen) { 72 | int c = fgetc(stream); 73 | if (c == EOF) return NULL; 74 | else if (c == header[seen]) seen++; 75 | else if (c == header[0]) seen = 1; 76 | else seen = 0; 77 | } 78 | 79 | buf_new(&buf); 80 | buf_put(&buf, header, headerlen); 81 | 82 | /* Look for the trailing '.' */ 83 | while(1) { 84 | int c = fgetc(stream); 85 | if (c == EOF) break; 86 | buf_putc(&buf, c); 87 | if (c == '.') break; 88 | } 89 | 90 | return buf.data; 91 | } 92 | -------------------------------------------------------------------------------- /toolkit/readotr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging Toolkit 3 | * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov 4 | * 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of version 2 of the GNU General Public License as 8 | * published by the Free Software Foundation. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #ifndef __READOTR_H__ 21 | #define __READOTR_H__ 22 | 23 | /* Read from the given stream until we see a complete OTR Key Exchange 24 | * or OTR Data message. Return a newly-allocated pointer to a copy of 25 | * this message, which the caller should free(). Returns NULL if no 26 | * such message could be found. */ 27 | char *readotr(FILE *stream); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /toolkit/sesskeys.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging Toolkit 3 | * Copyright (C) 2004-2014 Ian Goldberg, David Goulet, Chris Alexander, 4 | * Nikita Borisov 5 | * 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of version 2 of the GNU General Public License as 9 | * published by the Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | /* system headers */ 22 | #include 23 | 24 | /* libgcrypt headers */ 25 | #include 26 | 27 | static const char* DH1536_MODULUS_S = "0x" 28 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" 29 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" 30 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" 31 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" 32 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" 33 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" 34 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" 35 | "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF"; 36 | static const int DH1536_MOD_LEN_BITS = 1536; 37 | static const char *DH1536_GENERATOR_S = "0x02"; 38 | 39 | /* Generate the session id and the two encryption keys from our private 40 | * DH key and their public DH key. Also indicate in *high_endp if we 41 | * are the "high" end of the key exchange (set to 1) or the "low" end 42 | * (set to 0) */ 43 | void sesskeys_gen(unsigned char sessionid[20], unsigned char sendenc[16], 44 | unsigned char rcvenc[16], int *high_endp, gcry_mpi_t *our_yp, 45 | gcry_mpi_t our_x, gcry_mpi_t their_y) 46 | { 47 | gcry_mpi_t modulus, generator, secretv; 48 | unsigned char *secret; 49 | size_t secretlen; 50 | unsigned char hash[20]; 51 | int is_high; 52 | 53 | gcry_mpi_scan(&modulus, GCRYMPI_FMT_HEX, 54 | (const unsigned char *)DH1536_MODULUS_S, 0, NULL); 55 | gcry_mpi_scan(&generator, GCRYMPI_FMT_HEX, 56 | (const unsigned char *)DH1536_GENERATOR_S, 0, NULL); 57 | *our_yp = gcry_mpi_snew(DH1536_MOD_LEN_BITS); 58 | gcry_mpi_powm(*our_yp, generator, our_x, modulus); 59 | secretv = gcry_mpi_snew(DH1536_MOD_LEN_BITS); 60 | gcry_mpi_powm(secretv, their_y, our_x, modulus); 61 | gcry_mpi_release(generator); 62 | gcry_mpi_release(modulus); 63 | gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &secretlen, secretv); 64 | secret = malloc(secretlen + 5); 65 | 66 | secret[1] = (secretlen >> 24) & 0xff; 67 | secret[2] = (secretlen >> 16) & 0xff; 68 | secret[3] = (secretlen >> 8) & 0xff; 69 | secret[4] = (secretlen) & 0xff; 70 | gcry_mpi_print(GCRYMPI_FMT_USG, secret+5, secretlen, NULL, secretv); 71 | gcry_mpi_release(secretv); 72 | 73 | is_high = (gcry_mpi_cmp(*our_yp, their_y) > 0); 74 | 75 | /* Calculate the session id */ 76 | secret[0] = 0x00; 77 | gcry_md_hash_buffer(GCRY_MD_SHA1, hash, secret, secretlen+5); 78 | memmove(sessionid, hash, 20); 79 | 80 | /* Calculate the sending enc key */ 81 | secret[0] = is_high ? 0x01 : 0x02; 82 | gcry_md_hash_buffer(GCRY_MD_SHA1, hash, secret, secretlen+5); 83 | memmove(sendenc, hash, 16); 84 | 85 | /* Calculate the receiving enc key */ 86 | secret[0] = is_high ? 0x02 : 0x01; 87 | gcry_md_hash_buffer(GCRY_MD_SHA1, hash, secret, secretlen+5); 88 | memmove(rcvenc, hash, 16); 89 | 90 | *high_endp = is_high; 91 | free(secret); 92 | } 93 | 94 | /* Generate a MAC key from the corresponding encryption key */ 95 | void sesskeys_make_mac(unsigned char mackey[20], unsigned char enckey[16]) 96 | { 97 | gcry_md_hash_buffer(GCRY_MD_SHA1, mackey, enckey, 16); 98 | } 99 | -------------------------------------------------------------------------------- /toolkit/sesskeys.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging Toolkit 3 | * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov 4 | * 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of version 2 of the GNU General Public License as 8 | * published by the Free Software Foundation. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #ifndef __SESSKEYS_H__ 21 | #define __SESSKEYS_H__ 22 | 23 | /* Generate the session id and the two encryption keys from our private 24 | * DH key and their public DH key. Also indicate in *high_endp if we 25 | * are the "high" end of the key exchange (set to 1) or the "low" end 26 | * (set to 0) */ 27 | void sesskeys_gen(unsigned char sessionid[20], unsigned char sendenc[16], 28 | unsigned char rcvenc[16], int *high_endp, gcry_mpi_t *our_yp, 29 | gcry_mpi_t our_x, gcry_mpi_t their_y); 30 | 31 | /* Generate a MAC key from the corresponding encryption key */ 32 | void sesskeys_make_mac(unsigned char mackey[20], unsigned char enckey[16]); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /toolkit/sha1hmac.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging Toolkit 3 | * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov 4 | * 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of version 2 of the GNU General Public License as 8 | * published by the Free Software Foundation. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | /* system headers */ 21 | #include 22 | #include 23 | 24 | /* libgcrypt headers */ 25 | #include 26 | 27 | /* Implementation of SHA1-HMAC. We're rolling our own just to 28 | * double-check that the calls libotr makes to libgcrypt are in fact 29 | * doing the right thing. */ 30 | void sha1hmac(unsigned char digest[20], unsigned char key[20], 31 | unsigned char *data, size_t datalen) 32 | { 33 | unsigned char ipad[64], opad[64]; 34 | size_t i; 35 | gcry_md_hd_t sha1; 36 | gcry_error_t err; 37 | unsigned char hash[20]; 38 | 39 | memset(ipad, 0, 64); 40 | memset(opad, 0, 64); 41 | memmove(ipad, key, 20); 42 | memmove(opad, key, 20); 43 | for(i=0;i<64;++i) { 44 | ipad[i] ^= 0x36; 45 | opad[i] ^= 0x5c; 46 | } 47 | 48 | err = gcry_md_open(&sha1, GCRY_MD_SHA1, 0); 49 | if (err) { 50 | fprintf(stderr, "Error: %s\n", gcry_strerror(err)); 51 | exit(1); 52 | } 53 | gcry_md_write(sha1, ipad, 64); 54 | gcry_md_write(sha1, data, datalen); 55 | memmove(hash, gcry_md_read(sha1, 0), 20); 56 | gcry_md_reset(sha1); 57 | gcry_md_write(sha1, opad, 64); 58 | gcry_md_write(sha1, hash, 20); 59 | memmove(digest, gcry_md_read(sha1, 0), 20); 60 | gcry_md_close(sha1); 61 | } 62 | -------------------------------------------------------------------------------- /toolkit/sha1hmac.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging Toolkit 3 | * Copyright (C) 2004-2012 Ian Goldberg, Chris Alexander, Nikita Borisov 4 | * 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of version 2 of the GNU General Public License as 8 | * published by the Free Software Foundation. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #ifndef __SHA1HMAC_H__ 21 | #define __SHA1HMAC_H__ 22 | 23 | /* Implementation of SHA1-HMAC. We're rolling our own just to 24 | * double-check that the calls libotr makes to libgcrypt are in fact 25 | * doing the right thing. */ 26 | void sha1hmac(unsigned char digest[20], unsigned char key[20], 27 | unsigned char *data, size_t datalen); 28 | 29 | #endif 30 | --------------------------------------------------------------------------------