[Pkg-privacy-commits] [libotr] 35/225: Merged Version_3_Dev branch

Ximin Luo infinity0 at moszumanska.debian.org
Sat Aug 22 12:44:49 UTC 2015


This is an automated email from the git hooks/post-receive script.

infinity0 pushed a commit to branch master
in repository libotr.

commit 026379304f1a6d20405a19bfea177d181e681c0f
Author: cypherpunk <cypherpunk>
Date:   Tue Jul 24 19:18:06 2007 +0000

    Merged Version_3_Dev branch
---
 AUTHORS                 |   2 +-
 ChangeLog               |  40 +++
 NEWS                    |   5 +
 README                  |  12 +-
 UPGRADING               | 309 +++++++++++++++++
 configure.ac            |   4 +-
 libotr.m4               |   2 +-
 src/Makefile.am         |   4 +-
 src/auth.c              |   2 +-
 src/auth.h              |   2 +-
 src/b64.c               |  11 +-
 src/b64.h               |   4 +-
 src/context.c           |  12 +-
 src/context.h           |   6 +-
 src/dh.c                |   2 +-
 src/dh.h                |   2 +-
 src/mem.c               |   2 +-
 src/mem.h               |   2 +-
 src/message.c           | 302 ++++++++++++++++-
 src/message.h           |  37 +-
 src/privkey-t.h         |   2 +-
 src/privkey.c           |  21 +-
 src/privkey.h           |   8 +-
 src/proto.c             | 105 +++++-
 src/proto.h             |  12 +-
 src/serial.h            |   2 +-
 src/sm.c                | 884 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/sm.h                |  74 ++++
 src/tlv.c               |   2 +-
 src/tlv.h               |   8 +-
 src/userstate.c         |   2 +-
 src/userstate.h         |   2 +-
 src/version.h           |   6 +-
 toolkit/ctrmode.c       |   4 +-
 toolkit/ctrmode.h       |   4 +-
 toolkit/otr_mackey.c    |   2 +-
 toolkit/otr_modify.c    |   6 +-
 toolkit/otr_parse.c     |   2 +-
 toolkit/otr_readforge.c |   5 +-
 toolkit/otr_remac.c     |   2 +-
 toolkit/otr_sesskeys.c  |   2 +-
 toolkit/parse.c         |   4 +-
 toolkit/parse.h         |   2 +-
 toolkit/readotr.c       |   2 +-
 toolkit/readotr.h       |   2 +-
 toolkit/sesskeys.c      |   2 +-
 toolkit/sesskeys.h      |   2 +-
 toolkit/sha1hmac.c      |   2 +-
 toolkit/sha1hmac.h      |   2 +-
 49 files changed, 1850 insertions(+), 85 deletions(-)

diff --git a/AUTHORS b/AUTHORS
index 6cf447a..50a839d 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -2,6 +2,6 @@ Off-the-Record Messaging Library and Toolkit
 
 Authors:
 
-    Nikita Borisov and Ian Goldberg <otr at cypherpunks.ca>
+    Ian Goldberg, Chris Alexander, Nikita Borisov <otr at cypherpunks.ca>
 
 See the README file for mailing list information
diff --git a/ChangeLog b/ChangeLog
index ddf6af7..e0f1190 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,43 @@
+2007-07-24:
+
+	* src/proto.h:
+	* src/proto.c:
+	* src/message.c: Implemented fragmentation of large messages
+
+	* src/message.h: New callback for fragmentation
+
+	* src/privkey.h:
+	* src/privkey.c (otrl_privkey_fingerprint_raw): New function to
+	return a raw hash of an account's public key
+
+	* src/proto.c: Keep track of the API version number passed to
+	otrl_init()
+
+	* src/context.h:
+	* src/context.c:
+	* src/tlv.h:
+	* src/sm.h:
+	* src/sm.c: Implemented the Socialist Millionaires' Protocol for
+	authenticating buddies without using user-visible fingerprints
+
+	* src/b64.h:
+	* src/b64.c (decode, otrl_base64_decode): Corrected char vs.
+	unsigned char
+
+	* README:
+	* configure.ac:
+	* src/version.h: Change version number to 3.1.0
+
+	* Most files: Update copyright information
+
+2007-07-23
+
+	* src/message.h:
+	* src/message.c: Added account_name and account_name_free callbacks
+	to OtrlMessageAppOps to let the application choose how to
+	display the account name in OTR Error Messages.  Based on a
+	patch from Evan Schoenberg <evan.s at dreskin.net>.
+
 2006-07-24
 
 	* src/privkey.h:
diff --git a/NEWS b/NEWS
index d2df6be..c92d939 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,8 @@
+24 Jul 2007:
+- Added fragmentation support for large messages
+- Added new method for buddy authentication which does not require the
+  (explicit) use of fingerprints.
+
 02 Nov 2005:
 - Released 3.0.0
 
diff --git a/README b/README
index 19a3034..f974d43 100644
--- a/README
+++ b/README
@@ -1,5 +1,5 @@
 	      Off-the-Record Messaging Library and Toolkit
-			  v3.0.0,  2 Nov 2005
+			  v3.1.0, XX XXX 2007
 
 This is a library and toolkit which implements Off-the-Record (OTR) Messaging.
 
@@ -19,7 +19,7 @@ OTR allows you to have private conversations over IM by providing:
      is compromised.
 
 For more information on Off-the-Record Messaging, see
-http://www.cypherpunks.ca/otr/
+http://otr.cypherpunks.ca/
 
 LIBRARY USAGE
 
@@ -239,7 +239,7 @@ The Off-the-Record Messaging library (in the src directory) is
 covered by the following (LGPL) license:
 
     Off-the-Record Messaging library
-    Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+    Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
 			     <otr at cypherpunks.ca>
 
     This library is free software; you can redistribute it and/or
@@ -260,7 +260,7 @@ The Off-the-Record Messaging Toolkit (in the toolkit directory) is covered
 by the following (GPL) license:
 
     Off-the-Record Messaging Toolkit
-    Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+    Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
 		             <otr at cypherpunks.ca>
 
     This program is free software; you can redistribute it and/or modify
@@ -282,7 +282,7 @@ CONTACT
 To report problems, comments, suggestions, patches, etc., you can email
 the authors:
 
-Nikita Borisov and Ian Goldberg <otr at cypherpunks.ca>
+Ian Goldberg, Chris Alexander, and Nikita Borisov <otr at cypherpunks.ca>
 
 For more information on Off-the-Record Messaging, visit
-http://www.cypherpunks.ca/otr/
+http://otr.cypherpunks.ca/
diff --git a/UPGRADING b/UPGRADING
new file mode 100644
index 0000000..95c9dc9
--- /dev/null
+++ b/UPGRADING
@@ -0,0 +1,309 @@
+Table of Contents
+
+1. Preface/Introduction?
+2. Major Additions
+2.1. Fragmentation
+2.2. Socialist Millionaires' Protocol
+3. Required Changes
+3.1. OtrlMessageAppOps Callbacks
+3.1.1. Max Message Size
+3.1.2. Account Name
+3.2. Using Fragmentation
+3.3. Using SMP
+3.3.1. Initiating
+3.3.2. Responding
+3.3.3. Aborting
+3.3.4. Control Flow and Errors
+3.4. Miscellaneous
+
+1. Preface/Introduction?
+
+2. Major Additions
+
+This section describes the new features in OTR 3.1.0 along with a short
+history or motivation for each.
+
+2.1. Fragmentation
+
+Most IM networks supported by Pidgin have fixed maximum message sizes
+(MMS) of approximately 1-3 kB.  The longer messages in the initial key
+exchange and the new socialist millionaires' protocol may exceed these
+common MMS values.  To allow these protocols to work properly even over
+networks with low MMS values, support for fragmentation was added.
+
+OTR version 3.0.0 added support for recombining message fragments to
+recover the original message.  Now that users may be assumed to be able to
+handle message fragments, support for fragmenting and sending large
+messages has been added to OTR 3.1.0.
+
+2.2. Socialist Millionaires' Protocol
+
+In version 3.0.0, the only method available to authenticate a buddy was
+fingerprint verification.  However, many users who are unfamiliar with
+cryptography do not understand what a fingerprint is or how it is useful.
+Also, the verification itself relied on the user obtaining an authentic
+copy of the other party's fingerprint somehow.  The simplest way to do so
+may be to relay the displayed hexadecimal values during a phone call,
+but this is a large enough hassle that many users omit fingerprint
+verification altogether.
+
+To allow for a method of authentication that is both easier to understand
+and easier to use, OTR version 3.1.0 includes the Socialist Millionaires'
+Protocol (SMP).  SMP runs as follows: each user inputs a secret string,
+say "x" and "y".  They then exchange a series of messages which reveal
+the value of (x==y), but no additional information about the inputs.
+This allows users to determine whether they hold the same secret
+information with no danger of revealing that secret to an attacker.
+
+To see how this is useful for authentication in OTR, assume that Alice
+and Bob are chatting over OTR for the first time, though they know each
+other well in real life.  Alice may send Bob the following message:
+"Let's make our shared secret the name of that restaurant we both like
+in Ottawa."
+
+Now Alice and Bob run SMP.  If Alice is actually talking to Bob directly,
+then they will both type in the same restaurant name and SMP will return
+success (x==y).  However, if an attacker is impersonating Bob or trying
+to eavesdrop on the conversation, they will have no idea which restaurant
+Alice has in mind, and will type in an incorrect value, causing SMP to
+fail.  Note that for security reasons, the values compared in the SMP
+are actually hashes of several pieces of data, including both parties'
+fingerprints, along with their respective secrets.  The users, however,
+are never exposed to this additional data.
+
+Thus, SMP turns the problem of obtaining an authentic copy of a
+fingerprint into the much simpler problem of obtaining any shared secret,
+or simply of drawing on shared experiences to generate one.
+
+For detailed information on how SMP works, see the paper by Boudot,
+Schoenmakers and Traore titled "A Fair and Efficient Solution to the
+Socialist Millionaires Problem" (2001), on which our solution is based.
+
+3. Required Changes
+
+3.1. OtrlMessageAppOps Callbacks
+
+Three new callbacks have been added to the end of OtrlMessageAppOps.  If
+the version number passed to otrl_init is less than 3.1.0 then libotr will
+not call any of the new callbacks.  As well, you may disable individual
+callbacks by setting them to NULL.  In either case, libotr will revert to
+the standard behaviour of version 3.0.0.
+
+3.1.1. Max Message Size
+
+The first new callback has the following signature:
+
+    int (*max_message_size)(void *opdata, ConnContext *context);
+
+This method is called whenever a message is about to be sent with
+fragmentation enabled.  The return value is checked against the size of
+the message to be sent to determine whether fragmentation is necessary.
+
+Although the maximum message size depends on a number of factors, we
+found experimentally that the following rough values based solely on the
+(pidgin) protocol name work well:
+    "prpl-msn",   1409
+    "prpl-icq",   2346
+    "prpl-aim",   2343
+    "prpl-yahoo", 832
+    "prpl-gg",    1999
+    "prpl-irc",   417
+    "prpl-oscar", 2343
+
+Setting max_message_size to NULL will disable the fragmentation of all
+sent messages; returning 0 from this callback will disable fragmentation
+of a particular message.  The latter is useful, for example, for
+protocols like XMPP (Jabber) that do not require fragmentation at all.
+
+3.1.2. Account Name
+
+The other two new callbacks have the following signatures: 
+
+    const char *(*account_name)(void *opdata, const char *account,
+	    const char *protocol);
+    void (*account_name_free)(void *opdata, const char *account_name);
+
+Normally, if an error message needs to be sent from Alice to Bob,
+containing Alice's account name, the value of ConnContext->accountname
+will be used.  However, if this default name is unsuitable for your
+application, you can use the above methods to provide replacement values
+for displayed account names.
+
+account_name is called when libotr requires a human-readable version of
+an account name.  account_name_free is called once the name has
+been used, and the memory allocated by account_name (if any) must be
+released.
+
+Setting account_name to NULL will cause libotr to use
+ConnContext->accountname as the displayed name for an account.
+
+3.2. Using Fragmentation
+
+To make use of fragmentation, first make sure that the max_message_size
+callback described in 3.1.1. has been implemented.  Then, whenever you
+would normally send a message through your IM client, call
+otrl_message_fragment_and_send instead:
+
+gcry_error_t otrl_message_fragment_and_send(const OtrlMessageAppOps *ops,
+	void *opdata, ConnContext *context, const char *message,
+	OtrlFragmentPolicy fragPolicy, char **returnFragment);
+
+Here, message is the original, encrypted, unfragmented message.
+This method will break the message into fragments and send either all of
+them or almost all of them according to the OtrlFragmentPolicy:
+
+OTRL_FRAGMENT_SEND_ALL            sends all fragments at once
+OTRL_FRAGMENT_SEND_ALL_BUT_FIRST  sends all but the first fragment
+OTRL_FRAGMENT_SEND_ALL_BUT_LAST   sends all but the last fragment
+
+You may wish to use one of the latter two options if you still wish to
+pass a message through your IM client.  In this case, the unsent
+fragment will be returned in returnFragment and should be sent as a
+regular message.  In order to reassemble the fragments, however, they
+must be received in order, so at most one of the latter two options
+will result in readable messages.
+
+3.3. Using SMP
+
+Recall from section 2.2. above that SMP takes one input string from each
+user and outputs either failure or success.
+
+3.3.1. Initiating
+
+If you wish to initiate SMP for a user named Alice, you would use
+otrl_message_initiate_smp.
+
+void otrl_message_initiate_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
+	void *opdata, ConnContext *context, const unsigned char *secret,
+	size_t secretlen);
+
+Here, secret and secretlen describe the secret text as entered by Alice,
+for example, ("kitten", 6).  The other parameters are common to many
+otrl_message functions.  This method will cause a message to be sent
+containing an appropriate OTRL_TLV_SMP1 (see below).
+
+3.3.2. Responding
+
+If you wish to continue SMP by supplying the secret for a second user
+named Bob, you would use otrl_message_respond_smp:
+
+void otrl_message_respond_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
+	void *opdata, ConnContext *context, const unsigned char *secret,
+	size_t secretlen);
+
+The arguments for this method are the same as otrl_message_initiate_smp.
+This method will send a message with an appropriate OTRL_TLV_SMP2
+(see below).
+
+3.3.3. Aborting
+
+If you wish to abort SMP for any reason, including errors occuring
+during the protocol, you should use otrl_message_abort_smp:
+
+void otrl_message_abort_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
+	void *opdata, ConnContext *context);
+
+This method will cause the other user to abandon the current state of
+SMP by sending an appropriate OTRL_TLV_SMP_ABORT (see below).
+
+3.3.4. Control Flow and Errors
+ 
+The protocol itself consists of 4 messages passed between the two users,
+say Alice and Bob.  These messages are identified through their TLVs:
+
+OTRL_TLV_SMP1       The initial message, containing Alice's secret
+OTRL_TLV_SMP2       A response containing Bob's secret
+OTRL_TLV_SMP3       The next message in the chain, from Alice to Bob
+OTRL_TLV_SMP4       The final message in the chain, from Bob to Alice
+OTRL_TLV_SMP_ABORT  Indicates an error has occurred. Will reset SMP state
+
+To determine whether the protocol is proceeding correctly, additional
+information has been added to ConnContext.  You may access
+context->smstate->nextExpected to find out which TLV should come next,
+so you can compare this to what was actually received and take an
+appropriate action.  The value is of type NextExpectedSMP and could be
+any of:
+
+OTRL_SMP_EXPECT1   Next SMP TLV should be OTRL_TLV_SMP1
+OTRL_SMP_EXPECT2   Next SMP TLV should be OTRL_TLV_SMP2
+OTRL_SMP_EXPECT3   Next SMP TLV should be OTRL_TLV_SMP3
+OTRL_SMP_EXPECT4   Next SMP TLV should be OTRL_TLV_SMP4
+
+If at any point, an SMP TLV of an unexpected type is received, the
+protocol should abort.  Also, if the correct TLV type is received, then
+the state should be updated accordingly.  A typical control flow looks
+like this:
+
+    OtrlTLV *tlvs = NULL;
+    OtrlTLV *tlv = NULL;
+    [Initialize tlvs to the list of tlvs.  This can be done
+     as part of otrl_message_receiving.]
+
+    ConnContext *context = [correct context];
+    NextExpectedSMP nextMsg = context->smstate->nextExpected;
+
+    tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
+    if (tlv) {
+        if (nextMsg != OTRL_SMP_EXPECT1)
+	    [abort SMP];
+        else {
+	    [get secret from user and continue SMP];
+        }
+    }
+    tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
+    if (tlv) {
+        if (nextMsg != OTRL_SMP_EXPECT2)
+	    [abort SMP];
+        else {
+	    // If we received TLV2, we will send TLV3 and expect TLV4
+            context->smstate->nextExpected = OTRL_SMP_EXPECT4;
+        }
+    }
+    tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
+    if (tlv) {
+        if (nextMsg != OTRL_SMP_EXPECT3)
+	    [abort SMP];
+        else {
+	    // If we received TLV3, we will send TLV4
+            // We will not expect more messages, so prepare for next SMP
+            context->smstate->nextExpected = OTRL_SMP_EXPECT1;
+	    // Report result to user
+        }
+    }
+    tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
+    if (tlv) {
+        if (nextMsg != OTRL_SMP_EXPECT4)
+	    [abort SMP];
+        else {
+            // We will not expect more messages, so prepare for next SMP
+            context->smstate->nextExpected = OTRL_SMP_EXPECT1;
+	    // Report result to user
+        }
+    }
+    tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT);
+    if (tlv) {
+	// The message we are waiting for will not arrive, so reset
+        // and prepare for the next SMP
+	context->smstate->nextExpected = OTRL_SMP_EXPECT1;
+    }
+
+To report the result to the user after receiving OTRL_TLV_SMP3 or
+OTRL_TLV_SMP4, check whether context->active_fingerprint->trust is a
+non-empty string.  (That is, check that it's not NULL, and that its
+first character is not '\0'.)  If that is the case, then the SMP
+completed successfully.  Otherwise, the parties entered different secrets.
+
+3.4 Miscellaneous
+
+b64.h underwent a minor change in OTR 3.1.0.  It was purely a
+housekeeping change and should not require any changes to dependent code.
+
+The arguments to otrl_base64_encode and otrl_base64_decode did not agree
+in terms of which were of type char* and which were unsigned char*
+instead.  This has been corrected.  The new method signatures are:
+
+size_t otrl_base64_encode(char *base64data, const unsigned char *data,
+	size_t datalen);
+size_t otrl_base64_decode(unsigned char *data, const char *base64data,
+	size_t base64len);
+
diff --git a/configure.ac b/configure.ac
index 97300a0..1d3b44c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -16,8 +16,8 @@ dnl   For a backwards-incompatible API change (e.g. changing data structures):
 dnl     Change the libotr package version from a.b.c to (a+1).0.0
 dnl     Change the libotr libtool version from x:y:z to (x+1):0:0
 
-AM_INIT_AUTOMAKE(libotr, 3.0.0)
-LIBOTR_LIBTOOL_VERSION="2:0:0"
+AM_INIT_AUTOMAKE(libotr, 3.1.0)
+LIBOTR_LIBTOOL_VERSION="3:0:1"
 
 AC_SUBST(LIBOTR_LIBTOOL_VERSION)
 
diff --git a/libotr.m4 b/libotr.m4
index ec6d998..a21ac3e 100644
--- a/libotr.m4
+++ b/libotr.m4
@@ -1,6 +1,6 @@
 dnl
 dnl  Off-the-Record Messaging library
-dnl  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+dnl  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
 dnl                           <otr at cypherpunks.ca>
 dnl
 dnl  This library is free software; you can redistribute it and/or
diff --git a/src/Makefile.am b/src/Makefile.am
index 933aa11..c75fcbe 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -3,11 +3,11 @@ INCLUDES = @LIBGCRYPT_CFLAGS@
 lib_LTLIBRARIES = libotr.la
 
 libotr_la_SOURCES = privkey.c context.c proto.c b64.c dh.c mem.c message.c \
-		    userstate.c tlv.c auth.c
+		    userstate.c tlv.c auth.c sm.c
 
 libotr_la_LDFLAGS = -version-info @LIBOTR_LIBTOOL_VERSION@ @LIBS@ @LIBGCRYPT_LIBS@
 
 otrincdir = $(includedir)/libotr
 
 otrinc_HEADERS = b64.h context.h dh.h mem.h message.h privkey.h proto.h \
-		 version.h userstate.h tlv.h serial.h auth.h privkey-t.h
+		 version.h userstate.h tlv.h serial.h auth.h sm.h privkey-t.h
diff --git a/src/auth.c b/src/auth.c
index 56f053d..f8916ca 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
diff --git a/src/auth.h b/src/auth.h
index 8fa936c..a42c7e2 100644
--- a/src/auth.h
+++ b/src/auth.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
diff --git a/src/b64.c b/src/b64.c
index a461c88..4964c57 100644
--- a/src/b64.c
+++ b/src/b64.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
@@ -64,7 +64,7 @@ VERSION HISTORY:
 /*
 ** Translation Table as described in RFC1113
 */
-static const unsigned char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
 /*
 ** Translation Table to decode (created by author)
@@ -118,8 +118,7 @@ size_t otrl_base64_encode(char *base64data, const unsigned char *data,
     return base64len;
 }
 
-static size_t decode(unsigned char *out, const unsigned char *in,
-	size_t b64len)
+static size_t decode(unsigned char *out, const char *in, size_t b64len)
 {   
     size_t written = 0;
     unsigned char c = 0;
@@ -151,11 +150,11 @@ static size_t decode(unsigned char *out, const unsigned char *in,
  * The buffer data must contain at least (base64len / 4) * 3 bytes of
  * space.  This function will return the number of bytes actually used.
  */
-size_t otrl_base64_decode(char *data, const unsigned char *base64data,
+size_t otrl_base64_decode(unsigned char *data, const char *base64data,
 	size_t base64len)
 {
     size_t datalen = 0;
-    unsigned char b64[4];
+    char b64[4];
     size_t b64accum = 0;
 
     while(base64len > 0) {
diff --git a/src/b64.h b/src/b64.h
index 03d9b21..f7a1692 100644
--- a/src/b64.h
+++ b/src/b64.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
@@ -36,7 +36,7 @@ size_t otrl_base64_encode(char *base64data, const unsigned char *data,
  * The buffer data must contain at least (base64len / 4) * 3 bytes of
  * space.  This function will return the number of bytes actually used.
  */
-size_t otrl_base64_decode(char *data, const unsigned char *base64data,
+size_t otrl_base64_decode(unsigned char *data, const char *base64data,
 	size_t base64len);
 
 /*
diff --git a/src/context.c b/src/context.c
index 3205d3b..0b22f98 100644
--- a/src/context.c
+++ b/src/context.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
@@ -32,6 +32,7 @@ static ConnContext * new_context(const char * user, const char * accountname,
 	const char * protocol)
 {
     ConnContext * context;
+    OtrlSMState *smstate;
     context = malloc(sizeof(*context));
     assert(context != NULL);
     context->username = strdup(user);
@@ -43,6 +44,12 @@ static ConnContext * new_context(const char * user, const char * accountname,
     context->fragment_k = 0;
     context->msgstate = OTRL_MSGSTATE_PLAINTEXT;
     otrl_auth_new(&(context->auth));
+
+    smstate = malloc(sizeof(OtrlSMState));
+    assert(smstate != NULL);
+    otrl_sm_state_new(smstate);
+    context->smstate = smstate;
+
     context->fingerprint_root.fingerprint = NULL;
     context->fingerprint_root.context = context;
     context->fingerprint_root.next = NULL;
@@ -221,6 +228,7 @@ void otrl_context_force_finished(ConnContext *context)
     gcry_free(context->lastmessage);
     context->lastmessage = NULL;
     context->may_retransmit = 0;
+    otrl_sm_state_free(context->smstate);
 }
 
 /* Force a context into the OTRL_MSGSTATE_PLAINTEXT state. */
@@ -285,9 +293,11 @@ void otrl_context_forget(ConnContext *context)
     free(context->username);
     free(context->accountname);
     free(context->protocol);
+    free(context->smstate);
     context->username = NULL;
     context->accountname = NULL;
     context->protocol = NULL;
+    context->smstate = NULL;
 
     /* Free the application data, if it exists */
     if (context->app_data && context->app_data_free) {
diff --git a/src/context.h b/src/context.h
index 4aec29d..c680d33 100644
--- a/src/context.h
+++ b/src/context.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
@@ -24,6 +24,7 @@
 
 #include "dh.h"
 #include "auth.h"
+#include "sm.h"
 
 typedef enum {
     OTRL_MSGSTATE_PLAINTEXT,           /* Not yet started an encrypted
@@ -124,6 +125,9 @@ typedef struct context {
     void *app_data;
     /* A function to free the above data when we forget this context */
     void (*app_data_free)(void *);
+
+    OtrlSMState *smstate;              /* The state of the current
+                                          socialist millionaires exchange */
 } ConnContext;
 
 #include "userstate.h"
diff --git a/src/dh.c b/src/dh.c
index 9c22601..9a4a055 100644
--- a/src/dh.c
+++ b/src/dh.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
diff --git a/src/dh.h b/src/dh.h
index f41b023..a35edbb 100644
--- a/src/dh.h
+++ b/src/dh.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
diff --git a/src/mem.c b/src/mem.c
index d525577..b9fc384 100644
--- a/src/mem.c
+++ b/src/mem.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
diff --git a/src/mem.h b/src/mem.h
index f26f5fe..caa2343 100644
--- a/src/mem.h
+++ b/src/mem.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
diff --git a/src/message.c b/src/message.c
index 26bea8f..1439439 100644
--- a/src/message.c
+++ b/src/message.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
@@ -30,6 +30,10 @@
 #include "proto.h"
 #include "auth.h"
 #include "message.h"
+#include "sm.h"
+
+/* The API version */
+extern unsigned int otrl_api_version;
 
 /* How long after sending a packet should we wait to send a heartbeat? */
 #define HEARTBEAT_INTERVAL 60
@@ -221,7 +225,7 @@ gcry_error_t otrl_message_sending(OtrlUserState us,
 			protocol, recipient, "Your message was not sent.  "
 			"Either end your private conversation, or restart "
 			"it.")) && ops->notify) {
-		const char *fmt = "%s has already closed his private "
+		const char *fmt = "%s has already closed his/her private "
 		    "connection to you";
 		char *primary = malloc(strlen(fmt) + strlen(recipient) - 1);
 		if (primary) {
@@ -251,10 +255,11 @@ static gcry_error_t send_or_error_auth(const OtrlMessageAppOps *ops,
     if (!err) {
 	const char *msg = context->auth.lastauthmsg;
 	if (msg && *msg) {
-	    if (ops->inject_message) {
+	    otrl_message_fragment_and_send(ops, opdata, context, msg, OTRL_FRAGMENT_SEND_ALL, NULL);
+	    /*if (ops->inject_message) {
 		ops->inject_message(opdata, context->accountname,
 			context->protocol, context->username, msg);
-	    }
+	    }*/
 	}
     } else {
 	const char *buf_format = "Error setting up private conversation: %s";
@@ -456,11 +461,7 @@ static void maybe_resend(EncrData *edata)
 	    char *buf;
 
 	    /* Resend the message */
-	    if (edata->ops->inject_message) {
-		edata->ops->inject_message(edata->opdata,
-			edata->context->accountname, edata->context->protocol,
-			edata->context->username, resendmsg);
-	    }
+	    otrl_message_fragment_and_send(edata->ops, edata->opdata, edata->context, resendmsg, OTRL_FRAGMENT_SEND_ALL, NULL);
 	    free(resendmsg);
 	    edata->context->lastsent = now;
 
@@ -494,6 +495,127 @@ static void maybe_resend(EncrData *edata)
     }
 }
 
+/* Set the trust level based on the result of the SMP */
+static void set_smp_trust(const OtrlMessageAppOps *ops, void *opdata,
+	ConnContext *context, int trusted)
+{
+    otrl_context_set_trust(context->active_fingerprint, trusted ? "smp" : "");
+
+    /* Write the new info to disk, redraw the ui, and redraw the
+     * OTR buttons. */
+    if (ops->write_fingerprints) {
+	ops->write_fingerprints(opdata);
+    }
+}
+
+static void init_respond_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
+	void *opdata, ConnContext *context, const unsigned char *secret,
+	size_t secretlen, int initiating)
+{
+    unsigned char *smpmsg = NULL;
+    int smpmsglen;
+    unsigned char combined_secret[SM_DIGEST_SIZE];
+    gcry_error_t err;
+    unsigned char our_fp[20];
+    unsigned char *combined_buf;
+    size_t combined_buf_len;
+
+    if (!context || context->msgstate != OTRL_MSGSTATE_ENCRYPTED) return;
+
+    /*
+     * Construct the combined secret as a SHA256 hash of:
+     * Version byte (0x01), Initiator fingerprint (20 bytes),
+     * responder fingerprint (20 bytes), secure session id, input secret
+     */
+    otrl_privkey_fingerprint_raw(us, our_fp, context->accountname,
+	    context->protocol);
+
+    combined_buf_len = 41 + context->sessionid_len + secretlen;
+    combined_buf = malloc(combined_buf_len);
+    combined_buf[0] = 0x01;
+    if (initiating) {
+	memmove(combined_buf + 1, our_fp, 20);
+	memmove(combined_buf + 21,
+		context->active_fingerprint->fingerprint, 20);
+    } else {
+	memmove(combined_buf + 1,
+		context->active_fingerprint->fingerprint, 20);
+	memmove(combined_buf + 21, our_fp, 20);
+    }
+    memmove(combined_buf + 41, context->sessionid,
+	    context->sessionid_len);
+    memmove(combined_buf + 41 + context->sessionid_len,
+	    secret, secretlen);
+    gcry_md_hash_buffer(SM_HASH_ALGORITHM, combined_secret, combined_buf,
+	    combined_buf_len);
+    free(combined_buf);
+
+    if (initiating) {
+	otrl_sm_step1(context->smstate, combined_secret, SM_DIGEST_SIZE,
+		&smpmsg, &smpmsglen);
+    } else {
+	otrl_sm_step2b(context->smstate, combined_secret, SM_DIGEST_SIZE,
+		&smpmsg, &smpmsglen);
+    }
+
+    /* Send msg with next smp msg content */
+    OtrlTLV *sendtlv = otrl_tlv_new(initiating ? OTRL_TLV_SMP1 : OTRL_TLV_SMP2,
+	    smpmsglen, smpmsg);
+    char *sendsmp = NULL;
+    err = otrl_proto_create_data(&sendsmp, context, "", sendtlv,
+            OTRL_MSGFLAGS_IGNORE_UNREADABLE);
+    if (!err) {
+        /*  Send it, and set the next expected message to the
+	 *  logical response */
+        err = otrl_message_fragment_and_send(ops, opdata, context,
+		sendsmp, OTRL_FRAGMENT_SEND_ALL, NULL);
+        context->smstate->nextExpected =
+	    initiating ? OTRL_SMP_EXPECT2 : OTRL_SMP_EXPECT3;
+    }
+    free(sendsmp);
+    otrl_tlv_free(sendtlv);
+    free(smpmsg);
+}
+
+/* Initiate the Socialist Millionaires' Protocol */
+void otrl_message_initiate_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
+	void *opdata, ConnContext *context, const unsigned char *secret,
+	size_t secretlen)
+{
+    init_respond_smp(us, ops, opdata, context, secret, secretlen, 1);
+}
+
+/* Respond to a buddy initiating the Socialist Millionaires' Protocol */
+void otrl_message_respond_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
+	void *opdata, ConnContext *context, const unsigned char *secret,
+	size_t secretlen)
+{
+    init_respond_smp(us, ops, opdata, context, secret, secretlen, 0);
+}
+
+/* Abort the SMP.  Called when an unexpected SMP message breaks the
+ * normal flow. */
+void otrl_message_abort_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
+	void *opdata, ConnContext *context)
+{
+    context->smstate->nextExpected = OTRL_SMP_EXPECT1;
+
+    OtrlTLV *sendtlv = otrl_tlv_new(OTRL_TLV_SMP_ABORT, 0,
+	    (const unsigned char *)"");
+    char *sendsmp = NULL;
+    gcry_error_t err;
+    err = otrl_proto_create_data(&sendsmp,
+	    context, "", sendtlv,
+	    OTRL_MSGFLAGS_IGNORE_UNREADABLE);
+    if (!err) {
+	/* Send the abort signal so our buddy knows we've stopped */
+	err = otrl_message_fragment_and_send(ops, opdata, context,
+		sendsmp, OTRL_FRAGMENT_SEND_ALL, NULL);
+    }
+    free(sendsmp);
+    otrl_tlv_free(sendtlv);
+}
+
 /* Handle a message just received from the network.  It is safe to pass
  * all received messages to this routine.  add_appdata is a function
  * that will be called in the event that a new ConnContext is created.
@@ -766,11 +888,14 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
 	case OTRL_MSGTYPE_DATA:
 	    switch(context->msgstate) {
 		gcry_error_t err;
-		OtrlTLV *tlvs;
+		OtrlTLV *tlvs, *tlv;
 		char *plaintext;
 		char *buf;
-		char *format;
+		const char *format;
+		const char *displayaccountname;
 		unsigned char flags;
+		NextExpectedSMP nextMsg;
+
 		case OTRL_MSGSTATE_PLAINTEXT:
 		case OTRL_MSGSTATE_FINISHED:
 		    /* See if we're supposed to ignore this message in
@@ -806,16 +931,29 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
 		    }
 		    format = "?OTR Error: You sent encrypted "
 			    "data to %s, who wasn't expecting it.";
-		    buf = malloc(strlen(format) + strlen(context->accountname)
+		    if (otrl_api_version >= 0x00030100 &&
+			    ops->account_name) {
+			displayaccountname = ops->account_name(opdata,
+				context->accountname, protocol);
+		    } else {
+			displayaccountname = NULL;
+		    }
+		    buf = malloc(strlen(format) + strlen(displayaccountname ?
+				displayaccountname : context->accountname)
 			    - 1);
 		    if (buf) {
-			sprintf(buf, format, context->accountname);
+			sprintf(buf, format, displayaccountname ?
+				displayaccountname : context->accountname);
 			if (ops->inject_message) {
 			    ops->inject_message(opdata, accountname, protocol,
 				    sender, buf);
 			}
 			free(buf);
 		    }
+		    if (displayaccountname && otrl_api_version >= 0x00030100 &&
+			    ops->account_name_free) {
+			ops->account_name_free(opdata, displayaccountname);
+		    }
 
 		    break;
 
@@ -863,7 +1001,75 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
 		    if (otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED)) {
 			otrl_context_force_finished(context);
 		    }
-		    
+
+                    /* If TLVs contain SMP data, process it */
+		    nextMsg = context->smstate->nextExpected;
+                    tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
+		    if (tlv && nextMsg == OTRL_SMP_EXPECT1) {
+			/* We can only do the verification half now.
+			 * We must wait for the secret to be entered
+			 * to continue. */
+			otrl_sm_step2a(context->smstate, tlv->data, tlv->len);
+                    }
+                    tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
+		    if (tlv && nextMsg == OTRL_SMP_EXPECT2) {
+			unsigned char* nextmsg;
+			int nextmsglen;
+			OtrlTLV *sendtlv;
+			char *sendsmp;
+			otrl_sm_step3(context->smstate, tlv->data, tlv->len,
+					&nextmsg, &nextmsglen);
+			
+			/* Send msg with next smp msg content */
+			sendtlv = otrl_tlv_new(OTRL_TLV_SMP3, nextmsglen,
+				nextmsg);
+			err = otrl_proto_create_data(&sendsmp,
+				context, "", sendtlv,
+				OTRL_MSGFLAGS_IGNORE_UNREADABLE);
+			if (!err) {
+			    err = otrl_message_fragment_and_send(ops, opdata, context, sendsmp, OTRL_FRAGMENT_SEND_ALL, NULL);
+			}
+			free(sendsmp);
+			otrl_tlv_free(sendtlv);
+			free(nextmsg);
+                    }
+                    tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
+		    if (tlv && nextMsg == OTRL_SMP_EXPECT3) {
+			unsigned char* nextmsg;
+			int nextmsglen;
+			OtrlTLV *sendtlv;
+			char *sendsmp;
+			err = otrl_sm_step4(context->smstate, tlv->data,
+					tlv->len, &nextmsg, &nextmsglen);
+			/* Set trust level based on result */
+			set_smp_trust(ops, opdata, context,
+				(err == gcry_error(GPG_ERR_NO_ERROR)));
+			
+			/* Send msg with next smp msg content */
+			sendtlv = otrl_tlv_new(OTRL_TLV_SMP4, nextmsglen,
+				nextmsg);
+			err = otrl_proto_create_data(&sendsmp,
+				context, "", sendtlv,
+				OTRL_MSGFLAGS_IGNORE_UNREADABLE);
+			if (!err) {
+			    err = otrl_message_fragment_and_send(ops, opdata, context, sendsmp, OTRL_FRAGMENT_SEND_ALL, NULL);
+			}
+			free(sendsmp);
+			otrl_tlv_free(sendtlv);
+			free(nextmsg);
+                    }
+                    tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
+		    if (tlv && nextMsg == OTRL_SMP_EXPECT4) {
+			err = otrl_sm_step5(context->smstate, tlv->data,
+				tlv->len);
+			/* Set trust level based on result */
+			set_smp_trust(ops, opdata, context,
+				(err == gcry_error(GPG_ERR_NO_ERROR)));
+                    }
+                    tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT);
+		    if (tlv) {
+			context->smstate->nextExpected = OTRL_SMP_EXPECT1;
+		    }
 		    if (plaintext[0] == '\0') {
 			/* If it's a heartbeat (an empty message), don't
 			 * display it to the user, but log a debug message. */
@@ -1080,6 +1286,74 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
     return edata.ignore_message;
 }
 
+/* Send a message to the network, fragmenting first if necessary.
+ * All messages to be sent to the network should go through this
+ * method immediately before they are sent, ie after encryption. */
+gcry_error_t otrl_message_fragment_and_send(const OtrlMessageAppOps *ops,
+	void *opdata, ConnContext *context, const char *message,
+	OtrlFragmentPolicy fragPolicy, char **returnFragment)
+{
+    int mms = 0;
+    if (message && ops->inject_message) {
+        if (otrl_api_version >= 0x030100 && ops->max_message_size) {
+	    mms = ops->max_message_size(opdata, context);
+        }
+    	int msglen;
+    	msglen = strlen(message);
+
+	/* Don't incur overhead of fragmentation unless necessary */
+    	if(mms != 0 && msglen > mms) {
+	    char **fragments;
+	    gcry_error_t err;
+	    int fragment_count = ((msglen - 1) / (mms -19)) + 1;
+		/* like ceil(msglen/(mms - 19)) */
+	    err = otrl_proto_fragment_create(mms, fragment_count, &fragments,
+		    message);
+	    if (err) {
+		return err;
+	    }
+
+	    /* Determine which fragments to send and which to return
+	     * based on given Fragment Policy.  If the first fragment
+	     * should be returned instead of sent, store it. */
+	    if (fragPolicy == OTRL_FRAGMENT_SEND_ALL_BUT_FIRST) {
+		*returnFragment = strdup(fragments[0]);
+	    } else {
+		ops->inject_message(opdata, context->accountname,
+			context->protocol, context->username, fragments[0]);
+	    }
+	    int i;
+	    for (i=1; i<fragment_count-1; i++) {
+		ops->inject_message(opdata, context->accountname,
+			context->protocol, context->username, fragments[i]);
+	    }
+	    /* If the last fragment should be stored instead of sent,
+	     * store it */
+	    if (fragPolicy == OTRL_FRAGMENT_SEND_ALL_BUT_LAST) {
+		*returnFragment = strdup(fragments[fragment_count-1]);
+	    } else {
+		ops->inject_message(opdata, context->accountname,
+			context->protocol, context->username, fragments[fragment_count-1]);
+	    }
+	    /* Now free all fragment memory */
+	    otrl_proto_fragment_free(&fragments, fragment_count);
+
+	} else {
+	    /* No fragmentation necessary */
+	    if (fragPolicy == OTRL_FRAGMENT_SEND_ALL) {
+	    	ops->inject_message(opdata, context->accountname,
+		        context->protocol, context->username, message);
+	    } else {
+		/* Copy and return the entire given message. */
+		int l = strlen(message) + 1;
+		*returnFragment = malloc(sizeof(char)*l);
+		strcpy(*returnFragment, message);
+	    }
+	}
+    }
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
 /* Put a connection into the PLAINTEXT state, first sending the
  * other side a notice that we're doing so if we're currently ENCRYPTED,
  * and we think he's logged in. */
diff --git a/src/message.h b/src/message.h
index 56c8d04..9ef8e93 100644
--- a/src/message.h
+++ b/src/message.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
@@ -69,7 +69,7 @@ typedef struct s_OtrlMessageAppOps {
      * state), this is called so the UI can be updated. */
     void (*update_context_list)(void *opdata);
 
-    /* Return a newly-allocated string containing a human-friendly name
+    /* Return a newly allocated string containing a human-friendly name
      * for the given protocol id */
     const char *(*protocol_name)(void *opdata, const char *protocol);
 
@@ -97,6 +97,17 @@ typedef struct s_OtrlMessageAppOps {
     /* Log a message.  The passed message will end in "\n". */
     void (*log_message)(void *opdata, const char *message);
 
+    /* Find the maximum message size supported by this protocol. */
+    int (*max_message_size)(void *opdata, ConnContext *context);
+
+    /* Return a newly allocated string containing a human-friendly
+     * representation for the given account */
+    const char *(*account_name)(void *opdata, const char *account,
+	    const char *protocol);
+
+    /* Deallocate a string returned by account_name */
+    void (*account_name_free)(void *opdata, const char *account_name);
+
 } OtrlMessageAppOps;
 
 /* Deallocate a message allocated by other otrl_message_* routines. */
@@ -161,6 +172,13 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
 	void (*add_appdata)(void *data, ConnContext *context),
 	void *data);
 
+/* Send a message to the network, fragmenting first if necessary.
+ * All messages to be sent to the network should go through this
+ * method immediately before they are sent, ie after encryption. */
+gcry_error_t otrl_message_fragment_and_send(const OtrlMessageAppOps *ops,
+	void *opdata, ConnContext *context, const char *message,
+	OtrlFragmentPolicy fragPolicy, char **returnFragment);
+
 /* Put a connection into the PLAINTEXT state, first sending the
  * other side a notice that we're doing so if we're currently ENCRYPTED,
  * and we think he's logged in. */
@@ -168,4 +186,19 @@ void otrl_message_disconnect(OtrlUserState us, const OtrlMessageAppOps *ops,
 	void *opdata, const char *accountname, const char *protocol,
 	const char *username);
 
+/* Initiate the Socialist Millionaires' Protocol */
+void otrl_message_initiate_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
+	void *opdata, ConnContext *context, const unsigned char *secret,
+	size_t secretlen);
+
+/* Respond to a buddy initiating the Socialist Millionaires' Protocol */
+void otrl_message_respond_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
+	void *opdata, ConnContext *context, const unsigned char *secret,
+	size_t secretlen);
+
+/* Abort the SMP.  Called when an unexpected SMP message breaks the
+ * normal flow. */
+void otrl_message_abort_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
+	void *opdata, ConnContext *context);
+
 #endif
diff --git a/src/privkey-t.h b/src/privkey-t.h
index 57dc116..66a1033 100644
--- a/src/privkey-t.h
+++ b/src/privkey-t.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
diff --git a/src/privkey.c b/src/privkey.c
index d1adaf0..6ae44ee 100644
--- a/src/privkey.c
+++ b/src/privkey.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
@@ -71,6 +71,25 @@ char *otrl_privkey_fingerprint(OtrlUserState us, char fingerprint[45],
     return fingerprint;
 }
 
+/* Calculate a raw hash of our DSA public key.  Return it in the passed
+ * fingerprint buffer.  Return NULL on error, or a pointer to the given
+ * buffer on success. */
+unsigned char *otrl_privkey_fingerprint_raw(OtrlUserState us,
+	unsigned char hash[20], const char *accountname, const char *protocol)
+{
+    OtrlPrivKey *p = otrl_privkey_find(us, accountname, protocol);
+
+    if (p) {
+	/* Calculate the hash */
+	gcry_md_hash_buffer(GCRY_MD_SHA1, hash, p->pubkey_data,
+		p->pubkey_datalen);
+    } else {
+	return NULL;
+    }
+
+    return hash;
+}
+
 /* Create a public key block from a private key */
 static gcry_error_t make_pubkey(unsigned char **pubbufp, size_t *publenp,
 	gcry_sexp_t privkey)
diff --git a/src/privkey.h b/src/privkey.h
index 5073c18..65c73ef 100644
--- a/src/privkey.h
+++ b/src/privkey.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
@@ -33,6 +33,12 @@ void otrl_privkey_hash_to_human(char human[45], const unsigned char hash[20]);
 char *otrl_privkey_fingerprint(OtrlUserState us, char fingerprint[45],
 	const char *accountname, const char *protocol);
 
+/* Calculate a raw hash of our DSA public key.  Return it in the passed
+ * fingerprint buffer.  Return NULL on error, or a pointer to the given
+ * buffer on success. */
+unsigned char *otrl_privkey_fingerprint_raw(OtrlUserState us,
+	unsigned char hash[20], const char *accountname, const char *protocol);
+
 /* Read a sets of private DSA keys from a file on disk into the given
  * OtrlUserState. */
 gcry_error_t otrl_privkey_read(OtrlUserState us, const char *filename);
diff --git a/src/proto.c b/src/proto.c
index fdd2a99..e0e738f 100644
--- a/src/proto.c
+++ b/src/proto.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
@@ -37,11 +37,17 @@
 #include "tlv.h"
 #include "serial.h"
 
+/* For now, we need to know the API version the client is using so that
+ * we don't use any UI callbacks it hasn't set. */
+unsigned int otrl_api_version = 0;
+
 /* Initialize the OTR library.  Pass the version of the API you are
  * using. */
 void otrl_init(unsigned int ver_major, unsigned int ver_minor,
 	unsigned int ver_sub)
 {
+    unsigned int api_version;
+
     /* The major versions have to match, and you can't be using a newer
      * minor version than we expect. */
     if (ver_major != OTRL_VERSION_MAJOR || ver_minor > OTRL_VERSION_MINOR) {
@@ -52,11 +58,21 @@ void otrl_init(unsigned int ver_major, unsigned int ver_minor,
 	exit(1);
     }
 
+    /* Set the API version.  If we get called multiple times for some
+     * reason, take the smallest value. */
+    api_version = (ver_major << 16) | (ver_minor << 8) | (ver_sub);
+    if (otrl_api_version == 0 || otrl_api_version > api_version) {
+	otrl_api_version = api_version;
+    }
+
     /* Initialize the memory module */
     otrl_mem_init();
 
     /* Initialize the DH module */
     otrl_dh_init();
+
+    /* Initialize the SM module */
+    otrl_sm_init();
 }
 
 /* Return a pointer to a static string containing the version number of
@@ -199,10 +215,10 @@ char *otrl_proto_default_query_msg(const char *ourname, OtrlPolicy policy)
      * get passed to the main IM application for processing (and
      * free()ing). */
     const char *format = "?OTR%s\n<b>%s</b> has requested an "
-	    "<a href=\"http://www.cypherpunks.ca/otr/\">Off-the-Record "
+	    "<a href=\"http://otr.cypherpunks.ca/\">Off-the-Record "
 	    "private conversation</a>.  However, you do not have a plugin "
-	    "to support that.\nSee <a href=\"http://www.cypherpunks.ca/otr/\">"
-	    "http://www.cypherpunks.ca/otr/</a> for more information.";
+	    "to support that.\nSee <a href=\"http://otr.cypherpunks.ca/\">"
+	    "http://otr.cypherpunks.ca/</a> for more information.";
 
     /* Figure out the version tag */
     v1_supported = (policy & OTRL_POLICY_ALLOW_V1);
@@ -360,7 +376,7 @@ gcry_error_t otrl_proto_create_data(char **encmessagep, ConnContext *context,
     size_t reveallen = 20 * context->numsavedkeys;
     size_t base64len;
     char *base64buf = NULL;
-    char *msgbuf = NULL;
+    unsigned char *msgbuf = NULL;
     enum gcry_mpi_format format = GCRYMPI_FMT_USG;
     char *msgdup;
     int version = context->protocol_version;
@@ -461,7 +477,7 @@ gcry_error_t otrl_proto_create_data(char **encmessagep, ConnContext *context,
     base64len = ((buflen + 2) / 3) * 4;
     base64buf = malloc(5 + base64len + 1 + 1);
     if (base64buf == NULL) {
-	err = GPG_ERR_ENOMEM;
+	err = gcry_error(GPG_ERR_ENOMEM);
 	goto err;
     }
     memmove(base64buf, "?OTR:", 5);
@@ -705,7 +721,7 @@ gcry_error_t otrl_proto_accept_data(char **plaintextp, OtrlTLV **tlvsp,
     }
 
     gcry_mpi_release(sender_next_y);
-    *plaintextp = data;
+    *plaintextp = (char *)data;
 
     /* See if there are TLVs */
     nul = data;
@@ -811,3 +827,78 @@ OtrlFragmentResult otrl_proto_fragment_accumulate(char **unfragmessagep,
 
     return res;
 }
+
+/* Create a fragmented message. */
+gcry_error_t otrl_proto_fragment_create(int mms, int fragment_count,
+	char ***fragments, const char *message)
+{
+    char *fragdata;
+    int fragdatalen = 0;
+    unsigned short curfrag = 0;
+    int index = 0;
+    int msglen = strlen(message);
+    int headerlen = 19; /* Should vary by number of msgs */
+
+    char **fragmentarray = malloc(fragment_count * sizeof(char*));
+    if(!fragmentarray) return gcry_error(GPG_ERR_ENOMEM);
+    
+    /*
+     * Find the next message fragment and store it in the array.
+     */
+    for(curfrag = 1; curfrag <= fragment_count; curfrag++) {
+	if (msglen - index < mms - headerlen) {
+    	    fragdatalen = msglen - index;
+	} else {
+	    fragdatalen = mms - headerlen;
+	}
+	int i;
+	fragdata = malloc(fragdatalen + 1);
+    	if(!fragdata) {
+		for (i=0; i<curfrag-1; free(fragmentarray[i++])) {}
+    		free(fragmentarray);
+    		return gcry_error(GPG_ERR_ENOMEM);
+    	}
+    	strncpy(fragdata, message, fragdatalen);
+    	fragdata[fragdatalen] = 0;
+    	
+    	char *fragmentmsg = malloc(fragdatalen+headerlen+1);
+    	if(!fragmentmsg) {
+	    for (i=0; i<curfrag-1; free(fragmentarray[i++])) {}
+    	    free(fragmentarray);
+    	    free(fragdata);
+    	    return gcry_error(GPG_ERR_ENOMEM);
+    	}
+
+    	/* 
+    	 * Create the actual fragment and store it in the array
+    	 */
+    	snprintf(fragmentmsg, fragdatalen + headerlen, "?OTR,%05hu,%05hu,%s,", curfrag, fragment_count, fragdata);
+    	fragmentmsg[fragdatalen + headerlen] = 0;
+
+    	fragmentarray[curfrag-1] = fragmentmsg;
+    	
+    	free(fragdata);
+    	index += fragdatalen;
+	message += fragdatalen;
+    }
+    
+    *fragments = fragmentarray;
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Free a string array containing fragment messages. */
+void otrl_proto_fragment_free(char ***fragments, unsigned short arraylen)
+{
+    int i;
+    char **fragmentarray = *fragments;
+    if(fragmentarray) {
+	for(i = 0; i < arraylen; i++)
+	{
+	    if(fragmentarray[i]) {
+	    	free(fragmentarray[i]);
+	    }
+	}
+	free(fragmentarray);
+    }
+}
+
diff --git a/src/proto.h b/src/proto.h
index bb2cf08..4a988ba 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
@@ -84,6 +84,12 @@ typedef enum {
     OTRL_FRAGMENT_COMPLETE
 } OtrlFragmentResult;
 
+typedef enum {
+    OTRL_FRAGMENT_SEND_ALL,
+    OTRL_FRAGMENT_SEND_ALL_BUT_FIRST,
+    OTRL_FRAGMENT_SEND_ALL_BUT_LAST
+} OtrlFragmentPolicy;
+
 /* Initialize the OTR library.  Pass the version of the API you are
  * using. */
 void otrl_init(unsigned int ver_major, unsigned int ver_minor,
@@ -137,4 +143,8 @@ gcry_error_t otrl_proto_accept_data(char **plaintextp, OtrlTLV **tlvsp,
 OtrlFragmentResult otrl_proto_fragment_accumulate(char **unfragmessagep,
 	ConnContext *context, const char *msg);
 
+gcry_error_t otrl_proto_fragment_create(int mms, int fragment_count,
+	char ***fragments, const char *message);
+
+void otrl_proto_fragment_free(char ***fragments, unsigned short arraylen);
 #endif
diff --git a/src/serial.h b/src/serial.h
index 981a9e4..005b86c 100644
--- a/src/serial.h
+++ b/src/serial.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
diff --git a/src/sm.c b/src/sm.c
new file mode 100644
index 0000000..d997e42
--- /dev/null
+++ b/src/sm.c
@@ -0,0 +1,884 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2.1 of the GNU Lesser General
+ *  Public License as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* system headers */
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h> 
+
+/* libgcrypt headers */
+#include <gcrypt.h>
+
+/* libotr headers */
+#include "sm.h"
+#include "serial.h"
+
+static const int SM_MSG1_LEN = 6;
+static const int SM_MSG2_LEN = 11;
+static const int SM_MSG3_LEN = 8;
+static const int SM_MSG4_LEN = 3;
+
+/* The modulus p */
+static const char* SM_MODULUS_S = "0x"
+    "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+    "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+    "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+    "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+    "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+    "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+    "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+    "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF";
+/* The order of the group q = (p-1)/2 */
+static const char* SM_ORDER_S = "0x"
+    "7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68"
+    "948127044533E63A0105DF531D89CD9128A5043CC71A026E"
+    "F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122"
+    "F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6"
+    "F71C35FDAD44CFD2D74F9208BE258FF324943328F6722D9E"
+    "E1003E5C50B1DF82CC6D241B0E2AE9CD348B1FD47E9267AF"
+    "C1B2AE91EE51D6CB0E3179AB1042A95DCF6A9483B84B4B36"
+    "B3861AA7255E4C0278BA36046511B993FFFFFFFFFFFFFFFF";
+static const char *SM_GENERATOR_S = "0x02";
+static const int SM_MOD_LEN_BITS = 1536;
+static const int SM_MOD_LEN_BYTES = 192;
+
+static gcry_mpi_t SM_MODULUS = NULL;
+static gcry_mpi_t SM_GENERATOR = NULL;
+static gcry_mpi_t SM_ORDER = NULL;
+static gcry_mpi_t SM_MODULUS_MINUS_2 = NULL;
+
+/*
+ * Call this once, at plugin load time.  It sets up the modulus and
+ * generator MPIs.
+ */
+void otrl_sm_init(void)
+{
+    gcry_check_version(NULL);
+    gcry_mpi_scan(&SM_MODULUS, GCRYMPI_FMT_HEX, SM_MODULUS_S, 0, NULL);
+    gcry_mpi_scan(&SM_ORDER, GCRYMPI_FMT_HEX, SM_ORDER_S, 0, NULL);
+    gcry_mpi_scan(&SM_GENERATOR, GCRYMPI_FMT_HEX, SM_GENERATOR_S,
+	    0, NULL);
+    SM_MODULUS_MINUS_2 = gcry_mpi_new(SM_MOD_LEN_BITS);
+    gcry_mpi_sub_ui(SM_MODULUS_MINUS_2, SM_MODULUS, 2);
+}
+
+/*
+ * Initialize the fields of a SM state.
+ */
+void otrl_sm_state_new(OtrlSMState *smst)
+{
+    smst->secret = NULL;
+    smst->x2 = NULL;
+    smst->x3 = NULL;
+    smst->g1 = NULL;
+    smst->g2 = NULL;
+    smst->g3 = NULL;
+    smst->g3o = NULL;
+    smst->p = NULL;
+    smst->q = NULL;
+    smst->pab = NULL;
+    smst->qab = NULL;
+    smst->nextExpected = OTRL_SMP_EXPECT1;
+}
+
+/*
+ * Initialize the fields of a SM state.  Called the first time that
+ * a user begins an SMP session.
+ */
+void otrl_sm_state_init(OtrlSMState *smst)
+{
+    otrl_sm_state_free(smst);
+    smst->secret = gcry_mpi_new(SM_MOD_LEN_BITS);
+    smst->x2 = NULL;
+    smst->x3 = NULL;
+    smst->g1 = gcry_mpi_copy(SM_GENERATOR);
+    smst->g2 = gcry_mpi_new(SM_MOD_LEN_BITS);
+    smst->g3 = gcry_mpi_new(SM_MOD_LEN_BITS);
+    smst->g3o = gcry_mpi_new(SM_MOD_LEN_BITS);
+    smst->p = gcry_mpi_new(SM_MOD_LEN_BITS);
+    smst->q = gcry_mpi_new(SM_MOD_LEN_BITS);
+    smst->pab = gcry_mpi_new(SM_MOD_LEN_BITS);
+    smst->qab = gcry_mpi_new(SM_MOD_LEN_BITS);
+}
+
+/*
+ * Initialize the fields of a SM message1.
+ * [0] = g2a, [1] = c2, [2] = d2, [3] = g3a, [4] = c3, [5] = d3
+ */
+void otrl_sm_msg1_init(gcry_mpi_t **msg1)
+{
+    gcry_mpi_t *msg = malloc(SM_MSG1_LEN * sizeof(gcry_mpi_t));
+    msg[0] = gcry_mpi_new(SM_MOD_LEN_BITS);
+    msg[1] = NULL;
+    msg[2] = gcry_mpi_new(SM_MOD_LEN_BITS);
+    msg[3] = gcry_mpi_new(SM_MOD_LEN_BITS);
+    msg[4] = NULL;
+    msg[5] = gcry_mpi_new(SM_MOD_LEN_BITS);
+
+    *msg1 = msg;
+}
+
+/*
+ * Initialize the fields of a SM message2.
+ * [0] = g2b, [1] = c2, [2] = d2, [3] = g3b, [4] = c3, [5] = d3
+ * [6] = pb, [7] = qb, [8] = cp, [9] = d5, [10] = d6
+ */
+void otrl_sm_msg2_init(gcry_mpi_t **msg2)
+{
+    gcry_mpi_t *msg = malloc(SM_MSG2_LEN * sizeof(gcry_mpi_t));
+    msg[0] = gcry_mpi_new(SM_MOD_LEN_BITS);
+    msg[1] = NULL;
+    msg[2] = gcry_mpi_new(SM_MOD_LEN_BITS);
+    msg[3] = gcry_mpi_new(SM_MOD_LEN_BITS);
+    msg[4] = NULL;
+    msg[5] = gcry_mpi_new(SM_MOD_LEN_BITS);
+    msg[6] = gcry_mpi_new(SM_MOD_LEN_BITS);
+    msg[7] = gcry_mpi_new(SM_MOD_LEN_BITS);
+    msg[8] = NULL;
+    msg[9] = gcry_mpi_new(SM_MOD_LEN_BITS);
+    msg[10] = gcry_mpi_new(SM_MOD_LEN_BITS); 
+
+    *msg2 = msg;
+}
+
+/*
+ * Initialize the fields of a SM message3.
+ * [0] = pa, [1] = qa, [2] = cp, [3] = d5, [4] = d6, [5] = ra,
+ * [6] = cr, [7] = d7
+ */
+void otrl_sm_msg3_init(gcry_mpi_t **msg3)
+{
+    gcry_mpi_t *msg = malloc(SM_MSG3_LEN * sizeof(gcry_mpi_t));
+    msg[0] = gcry_mpi_new(SM_MOD_LEN_BITS);
+    msg[1] = gcry_mpi_new(SM_MOD_LEN_BITS);
+    msg[2] = NULL;
+    msg[3] = gcry_mpi_new(SM_MOD_LEN_BITS);
+    msg[4] = gcry_mpi_new(SM_MOD_LEN_BITS);
+    msg[5] = gcry_mpi_new(SM_MOD_LEN_BITS);
+    msg[6] = NULL;
+    msg[7] = gcry_mpi_new(SM_MOD_LEN_BITS);
+
+    *msg3 = msg;
+}
+
+/*
+ * Initialize the fields of a SM message4.
+ * [0] = rb, [1] = cr, [2] = d7
+ */
+void otrl_sm_msg4_init(gcry_mpi_t **msg4)
+{
+    gcry_mpi_t *msg = malloc(SM_MSG4_LEN * sizeof(gcry_mpi_t));
+    msg[0] = gcry_mpi_new(SM_MOD_LEN_BITS);
+    msg[1] = NULL;
+    msg[2] = gcry_mpi_new(SM_MOD_LEN_BITS);
+
+    *msg4 = msg;
+}
+
+/*
+ * Deallocate the contents of a OtrlSMState (but not the OtrlSMState
+ * itself)
+ */
+void otrl_sm_state_free(OtrlSMState *smst)
+{
+    gcry_mpi_release(smst->secret);
+    gcry_mpi_release(smst->x2);
+    gcry_mpi_release(smst->x3);
+    gcry_mpi_release(smst->g1);
+    gcry_mpi_release(smst->g2);
+    gcry_mpi_release(smst->g3);
+    gcry_mpi_release(smst->g3o);
+    gcry_mpi_release(smst->p);
+    gcry_mpi_release(smst->q);
+    gcry_mpi_release(smst->pab);
+    gcry_mpi_release(smst->qab);
+    otrl_sm_state_new(smst);
+}
+
+/*
+ * Deallocate the contents of a message
+ */
+void otrl_sm_msg_free(gcry_mpi_t **message, int msglen)
+{
+    gcry_mpi_t *msg = *message;
+    int i;
+    for (i=0; i<msglen; i++) {
+        gcry_mpi_release(msg[i]);
+    }
+    free(msg);
+    *message = NULL;
+}
+
+static gcry_mpi_t randomExponent(void)
+{
+    unsigned char *secbuf = NULL;
+    gcry_mpi_t randexpon = NULL;
+
+    /* Generate a random exponent */
+    secbuf = gcry_random_bytes_secure(SM_MOD_LEN_BYTES, GCRY_STRONG_RANDOM);
+    gcry_mpi_scan(&randexpon, GCRYMPI_FMT_USG, secbuf, SM_MOD_LEN_BYTES, NULL);
+    gcry_free(secbuf);
+
+    return randexpon;
+}
+
+/*
+ * Hash one or two mpis.  To hash only one mpi, b may be set to NULL.
+ */
+static gcry_error_t otrl_sm_hash(gcry_mpi_t* hash, int version,
+	const gcry_mpi_t a, const gcry_mpi_t b)
+{
+    unsigned char* input;
+    unsigned char output[SM_DIGEST_SIZE];
+    unsigned int sizea;
+    unsigned int sizeb;
+    unsigned int totalsize;
+    unsigned char* dataa;
+    unsigned char* datab;
+    
+    gcry_mpi_aprint(GCRYMPI_FMT_USG, &dataa, &sizea, a);
+    totalsize = 1 + 4 + sizea;
+    if (b) {
+	gcry_mpi_aprint(GCRYMPI_FMT_USG, &datab, &sizeb, b);
+	totalsize += 4 + sizeb;
+    } else {
+	sizeb = 0;
+    }
+
+    input = malloc(totalsize);
+    input[0] = (unsigned char)version;
+    input[1] = (unsigned char)((sizea >> 24) & 0xFF);
+    input[2] = (unsigned char)((sizea >> 16) & 0xFF);
+    input[3] = (unsigned char)((sizea >> 8) & 0xFF);
+    input[4] = (unsigned char)(sizea & 0xFF);
+    memmove(input + 5, dataa, sizea);
+    if (b) {
+	input[5 + sizea] = (unsigned char)((sizeb >> 24) & 0xFF);
+	input[6 + sizea] = (unsigned char)((sizeb >> 16) & 0xFF);
+	input[7 + sizea] = (unsigned char)((sizeb >> 8) & 0xFF);
+	input[8 + sizea] = (unsigned char)(sizeb & 0xFF);
+	memmove(input + 9 + sizea, datab, sizeb);
+    }
+
+    gcry_md_hash_buffer(SM_HASH_ALGORITHM, output, input, totalsize);
+    gcry_mpi_scan(hash, GCRYMPI_FMT_USG, output, SM_DIGEST_SIZE, NULL);
+    free(input);
+    input = NULL;
+
+    /* free memory */
+    gcry_free(dataa);
+    if (b) gcry_free(datab);
+
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* This method should be passed a pointer to an uninitialized buffer,
+ * and a list of mpis with a list length.  When returns, the buffer will
+ * point to newly-allocated memory (using malloc) containing a
+ * reversible serialization. */
+static gcry_error_t serialize_mpi_array(unsigned char **buffer, int *buflen,
+	unsigned int count, gcry_mpi_t *mpis)
+{
+    size_t totalsize = 0, lenp, nextsize;
+    unsigned int i, j;
+    size_t *list_sizes = malloc(count * sizeof(size_t));
+    unsigned char **tempbuffer = malloc(count * sizeof(unsigned char *));
+    unsigned char *bufp;
+
+    for (i=0; i<count; i++) {
+        gcry_mpi_aprint(GCRYMPI_FMT_USG, &(tempbuffer[i]), &(list_sizes[i]),
+		mpis[i]);
+        totalsize += list_sizes[i];
+    }
+
+    *buflen = (count+1)*4 + totalsize;
+    *buffer = malloc(*buflen * sizeof(char));
+
+    bufp = *buffer;
+    lenp = totalsize;
+
+    write_int(count);
+    for(i=0; i<count; i++)
+    {
+        nextsize = list_sizes[i];
+        write_int(nextsize);
+        
+        for(j=0; j<nextsize; j++)
+            bufp[j] = tempbuffer[i][j];
+        
+	bufp += nextsize;
+        lenp -= nextsize;
+        gcry_free(tempbuffer[i]);
+    }
+    free(tempbuffer);
+    free(list_sizes);
+
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Takes a buffer containing serialized and concatenated mpis
+ * and converts it to an array of gcry_mpi_t structs.
+ * The buffer is assumed to consist of a 4-byte int containing the
+ * number of mpis in the array, followed by {size, data} pairs for
+ * each mpi.  If malformed, method returns GCRY_ERROR_INV_VALUE */
+static gcry_error_t unserialize_mpi_array(gcry_mpi_t **mpis,
+	unsigned int expcount, const unsigned char *buffer, const int buflen)
+{
+    int i;
+    int lenp = buflen;
+    unsigned int thecount = 0;
+    const unsigned char* bufp = buffer;
+    *mpis = NULL;
+
+    read_int(thecount);
+    if (thecount != expcount) goto invval;
+
+    *mpis = malloc(thecount * sizeof(gcry_mpi_t));
+
+    for (i=0; i<thecount; i++) {
+	(*mpis)[i] = NULL;
+    }
+
+    for (i=0; i<thecount; i++) {
+	read_mpi((*mpis)[i]);
+    }
+
+    return gcry_error(GPG_ERR_NO_ERROR);
+
+invval:
+    if (*mpis) {
+	for (i=0; i<thecount; i++) {
+	    gcry_mpi_release((*mpis)[i]);
+	}
+	free(*mpis);
+	*mpis = NULL;
+    }
+    return gcry_error(GPG_ERR_INV_VALUE);
+}
+
+/* Check that an MPI is in the right range to be a (non-unit) group
+ * element */
+static int check_group_elem(gcry_mpi_t g)
+{
+    if (gcry_mpi_cmp_ui(g, 2) < 0 ||
+	    gcry_mpi_cmp(g, SM_MODULUS_MINUS_2) > 0) {
+	return 1;
+    }
+    return 0;
+}
+
+/* Check that an MPI is in the right range to be a (non-zero) exponent */
+static int check_expon(gcry_mpi_t x)
+{
+    if (gcry_mpi_cmp_ui(x, 1) < 0 ||
+	    gcry_mpi_cmp(x, SM_ORDER) >= 0) {
+	return 1;
+    }
+    return 0;
+}
+
+/*
+ * Proof of knowledge of a discrete logarithm
+ */
+static gcry_error_t otrl_sm_proof_know_log(gcry_mpi_t *c, gcry_mpi_t *d, const gcry_mpi_t g, const gcry_mpi_t x, int version)
+{
+    gcry_mpi_t r = randomExponent();
+    gcry_mpi_t temp = gcry_mpi_new(SM_MOD_LEN_BITS);
+    gcry_mpi_powm(temp, g, r, SM_MODULUS);
+    otrl_sm_hash(c, version, temp, NULL);
+    gcry_mpi_mulm(temp, x, *c, SM_ORDER);
+    gcry_mpi_subm(*d, r, temp, SM_ORDER);
+    gcry_mpi_release(temp);
+    gcry_mpi_release(r);
+
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/*
+ * Verify a proof of knowledge of a discrete logarithm.  Checks that c = h(g^d x^c)
+ */
+static int otrl_sm_check_know_log(const gcry_mpi_t c, const gcry_mpi_t d, const gcry_mpi_t g, const gcry_mpi_t x, int version)
+{
+    gcry_mpi_t gd = gcry_mpi_new(SM_MOD_LEN_BITS);  /* g^d */
+    gcry_mpi_t xc = gcry_mpi_new(SM_MOD_LEN_BITS);  /* x^c */
+    gcry_mpi_t gdxc = gcry_mpi_new(SM_MOD_LEN_BITS);   /* (g^d x^c) */
+    gcry_mpi_t hgdxc = NULL;   /* h(g^d x^c) */
+
+    gcry_mpi_powm(gd, g, d, SM_MODULUS);
+    gcry_mpi_powm(xc, x, c, SM_MODULUS);
+    gcry_mpi_mulm(gdxc, gd, xc, SM_MODULUS);
+    otrl_sm_hash(&hgdxc, version, gdxc, NULL);
+    
+    int comp = gcry_mpi_cmp(hgdxc, c);
+    gcry_mpi_release(gd);
+    gcry_mpi_release(xc);
+    gcry_mpi_release(gdxc);
+    gcry_mpi_release(hgdxc);
+
+    return comp;
+}
+
+/*
+ * Proof of knowledge of coordinates with first components being equal
+ */
+static gcry_error_t otrl_sm_proof_equal_coords(gcry_mpi_t *c, gcry_mpi_t *d1, gcry_mpi_t *d2, const OtrlSMState *state, const gcry_mpi_t r, int version)
+{
+    gcry_mpi_t r1 = randomExponent();
+    gcry_mpi_t r2 = randomExponent();
+    gcry_mpi_t temp1 = gcry_mpi_new(SM_MOD_LEN_BITS);
+    gcry_mpi_t temp2 = gcry_mpi_new(SM_MOD_LEN_BITS);
+
+    /* Compute the value of c, as c = h(g3^r1, g1^r1 g2^r2) */
+    gcry_mpi_powm(temp1, state->g1, r1, SM_MODULUS);
+    gcry_mpi_powm(temp2, state->g2, r2, SM_MODULUS);
+    gcry_mpi_mulm(temp2, temp1, temp2, SM_MODULUS);
+    gcry_mpi_powm(temp1, state->g3, r1, SM_MODULUS);
+    otrl_sm_hash(c, version, temp1, temp2);
+
+    /* Compute the d values, as d1 = r1 - r c, d2 = r2 - secret c */
+    gcry_mpi_mulm(temp1, r, *c, SM_ORDER);
+    gcry_mpi_subm(*d1, r1, temp1, SM_ORDER);
+
+    gcry_mpi_mulm(temp1, state->secret, *c, SM_ORDER);
+    gcry_mpi_subm(*d2, r2, temp1, SM_ORDER);
+
+    /* All clear */
+    gcry_mpi_release(r1);
+    gcry_mpi_release(r2);
+    gcry_mpi_release(temp1);
+    gcry_mpi_release(temp2);
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/*
+ * Verify a proof of knowledge of coordinates with first components being equal
+ */
+static gcry_error_t otrl_sm_check_equal_coords(const gcry_mpi_t c, const gcry_mpi_t d1, const gcry_mpi_t d2, const gcry_mpi_t p, const gcry_mpi_t q, const OtrlSMState *state, int version)
+{
+    gcry_mpi_t temp1 = gcry_mpi_new(SM_MOD_LEN_BITS);
+    gcry_mpi_t temp2 = gcry_mpi_new(SM_MOD_LEN_BITS);
+    gcry_mpi_t temp3 = gcry_mpi_new(SM_MOD_LEN_BITS);
+    gcry_mpi_t cprime = NULL;
+
+    /* To verify, we test that hash(g3^d1 * p^c, g1^d1 * g2^d2 * q^c) = c
+     * If indeed c = hash(g3^r1, g1^r1 g2^r2), d1 = r1 - r*c,
+     * d2 = r2 - secret*c.  And if indeed p = g3^r, q = g1^r * g2^secret
+     * Then we should have that:
+     *   hash(g3^d1 * p^c, g1^d1 * g2^d2 * q^c)
+     * = hash(g3^(r1 - r*c + r*c), g1^(r1 - r*c + q*c) *
+     *      g2^(r2 - secret*c + secret*c))
+     * = hash(g3^r1, g1^r1 g2^r2)
+     * = c
+     */
+    gcry_mpi_powm(temp2, state->g3, d1, SM_MODULUS);
+    gcry_mpi_powm(temp3, p, c, SM_MODULUS);
+    gcry_mpi_mulm(temp1, temp2, temp3, SM_MODULUS);
+
+    gcry_mpi_powm(temp2, state->g1, d1, SM_MODULUS);
+    gcry_mpi_powm(temp3, state->g2, d2, SM_MODULUS);
+    gcry_mpi_mulm(temp2, temp2, temp3, SM_MODULUS);
+    gcry_mpi_powm(temp3, q, c, SM_MODULUS);
+    gcry_mpi_mulm(temp2, temp3, temp2, SM_MODULUS);
+
+    otrl_sm_hash(&cprime, version, temp1, temp2);
+
+    int comp = gcry_mpi_cmp(c, cprime);
+    gcry_mpi_release(temp1);
+    gcry_mpi_release(temp2);
+    gcry_mpi_release(temp3);
+    gcry_mpi_release(cprime);
+
+    return comp;
+}
+
+/*
+ * Proof of knowledge of logs with exponents being equal
+ */
+static gcry_error_t otrl_sm_proof_equal_logs(gcry_mpi_t *c, gcry_mpi_t *d, OtrlSMState *state, int version)
+{
+    gcry_mpi_t r = randomExponent();
+    gcry_mpi_t temp1 = gcry_mpi_new(SM_MOD_LEN_BITS);
+    gcry_mpi_t temp2 = gcry_mpi_new(SM_MOD_LEN_BITS);
+
+    /* Compute the value of c, as c = h(g1^r, (Qa/Qb)^r) */
+    gcry_mpi_powm(temp1, state->g1, r, SM_MODULUS);
+    gcry_mpi_powm(temp2, state->qab, r, SM_MODULUS);
+    otrl_sm_hash(c, version, temp1, temp2);
+
+    /* Compute the d values, as d = r - x3 c */
+    gcry_mpi_mulm(temp1, state->x3, *c, SM_ORDER);
+    gcry_mpi_subm(*d, r, temp1, SM_ORDER);
+
+    /* All clear */
+    gcry_mpi_release(r);
+    gcry_mpi_release(temp1);
+    gcry_mpi_release(temp2);
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/*
+ * Verify a proof of knowledge of logs with exponents being equal
+ */
+static gcry_error_t otrl_sm_check_equal_logs(const gcry_mpi_t c, const gcry_mpi_t d, const gcry_mpi_t r, const OtrlSMState *state, int version)
+{
+    gcry_mpi_t temp1 = gcry_mpi_new(SM_MOD_LEN_BITS);
+    gcry_mpi_t temp2 = gcry_mpi_new(SM_MOD_LEN_BITS);
+    gcry_mpi_t temp3 = gcry_mpi_new(SM_MOD_LEN_BITS);
+    gcry_mpi_t cprime = NULL;
+
+    /* Here, we recall the exponents used to create g3.
+     * If we have previously seen g3o = g1^x where x is unknown
+     * during the DH exchange to produce g3, then we may proceed with:
+     * 
+     * To verify, we test that hash(g1^d * g3o^c, qab^d * r^c) = c
+     * If indeed c = hash(g1^r1, qab^r1), d = r1- x * c
+     * And if indeed r = qab^x
+     * Then we should have that:
+     *   hash(g1^d * g3o^c, qab^d r^c)
+     * = hash(g1^(r1 - x*c + x*c), qab^(r1 - x*c + x*c))
+     * = hash(g1^r1, qab^r1)
+     * = c
+     */
+    gcry_mpi_powm(temp2, state->g1, d, SM_MODULUS);
+    gcry_mpi_powm(temp3, state->g3o, c, SM_MODULUS);
+    gcry_mpi_mulm(temp1, temp2, temp3, SM_MODULUS);
+
+    gcry_mpi_powm(temp3, state->qab, d, SM_MODULUS);
+    gcry_mpi_powm(temp2, r, c, SM_MODULUS);
+    gcry_mpi_mulm(temp2, temp3, temp2, SM_MODULUS);
+
+    otrl_sm_hash(&cprime, version, temp1, temp2);
+
+    int comp = gcry_mpi_cmp(c, cprime);
+    gcry_mpi_release(temp1);
+    gcry_mpi_release(temp2);
+    gcry_mpi_release(temp3);
+    gcry_mpi_release(cprime);
+
+    return comp;
+}
+
+/* Create first message in SMP exchange.  Input is Alice's secret value
+ * which this protocol aims to compare to Bob's.  Output is a serialized
+ * mpi array whose elements correspond to the following:
+ * [0] = g2a, Alice's half of DH exchange to determine g2
+ * [1] = c2, [2] = d2, Alice's ZK proof of knowledge of g2a exponent
+ * [3] = g3a, Alice's half of DH exchange to determine g3
+ * [4] = c3, [5] = d3, Alice's ZK proof of knowledge of g3a exponent */
+gcry_error_t otrl_sm_step1(OtrlSMAliceState *astate,
+	const unsigned char* secret, int secretlen,
+	unsigned char** output, int* outputlen)
+{
+    /* Initialize the sm state or update the secret */
+    gcry_mpi_t secret_mpi = NULL;
+    gcry_mpi_scan(&secret_mpi, GCRYMPI_FMT_USG, secret, secretlen, NULL);
+
+    if (! astate->g1) {
+        otrl_sm_state_init(astate);
+    }
+    gcry_mpi_set(astate->secret, secret_mpi);
+    gcry_mpi_release(secret_mpi);
+
+    gcry_mpi_t *msg1;
+    otrl_sm_msg1_init(&msg1);
+
+    astate->x2 = randomExponent();
+    astate->x3 = randomExponent();
+
+    gcry_mpi_powm(msg1[0], astate->g1, astate->x2, SM_MODULUS);
+    otrl_sm_proof_know_log(&(msg1[1]), &(msg1[2]), astate->g1, astate->x2, 1);
+
+    gcry_mpi_powm(msg1[3], astate->g1, astate->x3, SM_MODULUS);
+    otrl_sm_proof_know_log(&(msg1[4]), &(msg1[5]), astate->g1, astate->x3, 2);
+
+    serialize_mpi_array(output, outputlen, SM_MSG1_LEN, msg1);
+    otrl_sm_msg_free(&msg1, SM_MSG1_LEN);
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Receive the first message in SMP exchange, which was generated by
+ * otrl_sm_step1.  Input is saved until the user inputs their secret
+ * information.  No output. */
+gcry_error_t otrl_sm_step2a(OtrlSMBobState *bstate, const unsigned char* input, const int inputlen)
+{
+    gcry_mpi_t *msg1;
+    gcry_error_t err;
+
+    /* Initialize the sm state if needed */
+    if (! bstate->g1) {
+	otrl_sm_state_init(bstate);
+    }
+
+    /* Read from input to find the mpis */
+    err = unserialize_mpi_array(&msg1, SM_MSG1_LEN, input, inputlen);
+    
+    if (err != gcry_error(GPG_ERR_NO_ERROR)) return err;
+
+    if (check_group_elem(msg1[0]) || check_expon(msg1[2]) ||
+	    check_group_elem(msg1[3]) || check_expon(msg1[5])) {
+        return gcry_error(GPG_ERR_INV_VALUE);
+    }
+
+    /* Store Alice's g3a value for later in the protocol */
+    gcry_mpi_set(bstate->g3o, msg1[3]);
+    
+    /* Verify Alice's proofs */
+    if (otrl_sm_check_know_log(msg1[1], msg1[2], bstate->g1, msg1[0], 1) || 
+        otrl_sm_check_know_log(msg1[4], msg1[5], bstate->g1, msg1[3], 2)) {
+        return gcry_error(GPG_ERR_INV_VALUE);
+    }
+
+    /* Create Bob's half of the generators g2 and g3 */
+    bstate->x2 = randomExponent();
+    bstate->x3 = randomExponent();
+
+    /* Combine the two halves from Bob and Alice and determine g2 and g3 */
+    gcry_mpi_powm(bstate->g2, msg1[0], bstate->x2, SM_MODULUS);
+    gcry_mpi_powm(bstate->g3, msg1[3], bstate->x3, SM_MODULUS);
+
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Create second message in SMP exchange.  Input is Bob's secret value.
+ * Information from earlier steps in the exchange is taken from Bob's
+ * state.  Output is a serialized mpi array whose elements correspond
+ * to the following:
+ * [0] = g2b, Bob's half of DH exchange to determine g2
+ * [1] = c2, [2] = d2, Bob's ZK proof of knowledge of g2b exponent
+ * [3] = g3b, Bob's half of DH exchange to determine g3
+ * [4] = c3, [5] = d3, Bob's ZK proof of knowledge of g3b exponent
+ * [6] = pb, [7] = qb, Bob's halves of the (Pa/Pb) and (Qa/Qb) values
+ * [8] = cp, [9] = d5, [10] = d6, Bob's ZK proof that pb, qb formed correctly */
+gcry_error_t otrl_sm_step2b(OtrlSMBobState *bstate, const unsigned char* secret, int secretlen, unsigned char **output, int* outputlen)
+{
+    /* Convert the given secret to the proper form and store it */
+    gcry_mpi_t *msg2;
+    gcry_mpi_t secret_mpi = NULL;
+    gcry_mpi_scan(&secret_mpi, GCRYMPI_FMT_USG, secret, secretlen, NULL);
+    gcry_mpi_set(bstate->secret, secret_mpi);
+    gcry_mpi_release(secret_mpi);
+
+    otrl_sm_msg2_init(&msg2);
+
+    gcry_mpi_powm(msg2[0], bstate->g1, bstate->x2, SM_MODULUS);
+    otrl_sm_proof_know_log(&(msg2[1]), &(msg2[2]), bstate->g1, bstate->x2, 3);
+
+    gcry_mpi_powm(msg2[3], bstate->g1, bstate->x3, SM_MODULUS);
+    otrl_sm_proof_know_log(&(msg2[4]), &(msg2[5]), bstate->g1, bstate->x3, 4);
+
+    /* Calculate P and Q values for Bob */
+    gcry_mpi_t r = randomExponent();
+    gcry_mpi_t qb1 = gcry_mpi_new(SM_MOD_LEN_BITS);
+    gcry_mpi_t qb2 = gcry_mpi_new(SM_MOD_LEN_BITS);
+    gcry_mpi_powm(bstate->p, bstate->g3, r, SM_MODULUS);
+    gcry_mpi_set(msg2[6], bstate->p);
+    gcry_mpi_powm(qb1, bstate->g1, r, SM_MODULUS);
+    gcry_mpi_powm(qb2, bstate->g2, bstate->secret, SM_MODULUS);
+    gcry_mpi_mulm(bstate->q, qb1, qb2, SM_MODULUS);
+    gcry_mpi_set(msg2[7], bstate->q);
+
+    otrl_sm_proof_equal_coords(&(msg2[8]), &(msg2[9]), &(msg2[10]), bstate, r, 5);
+
+    /* Convert to serialized form */
+    serialize_mpi_array(output, outputlen, SM_MSG2_LEN, msg2);
+
+    /* Free up memory for unserialized and intermediate values */
+    gcry_mpi_release(r);
+    gcry_mpi_release(qb1);
+    gcry_mpi_release(qb2);
+    otrl_sm_msg_free(&msg2, SM_MSG2_LEN);
+
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Create third message in SMP exchange.  Input is a message generated
+ * by otrl_sm_step2b. Output is a serialized mpi array whose elements
+ * correspond to the following:
+ * [0] = pa, [1] = qa, Alice's halves of the (Pa/Pb) and (Qa/Qb) values
+ * [2] = cp, [3] = d5, [4] = d6, Alice's ZK proof that pa, qa formed correctly
+ * [5] = ra, calculated as (Qa/Qb)^x3 where x3 is the exponent used in g3a
+ * [6] = cr, [7] = d7, Alice's ZK proof that ra is formed correctly */
+gcry_error_t otrl_sm_step3(OtrlSMAliceState *astate, const unsigned char* input, const int inputlen, unsigned char **output, int* outputlen)
+{
+    /* Read from input to find the mpis */
+    gcry_mpi_t *msg2;
+    gcry_mpi_t *msg3;
+    gcry_error_t err;
+    err = unserialize_mpi_array(&msg2, SM_MSG2_LEN, input, inputlen);
+    
+    if (err != gcry_error(GPG_ERR_NO_ERROR)) return err;
+
+    if (check_group_elem(msg2[0]) || check_group_elem(msg2[3]) ||
+	    check_group_elem(msg2[6]) || check_group_elem(msg2[7]) ||
+	    check_expon(msg2[2]) || check_expon(msg2[5]) ||
+	    check_expon(msg2[9]) || check_expon(msg2[10])) {
+        return gcry_error(GPG_ERR_INV_VALUE);
+    }
+
+    otrl_sm_msg3_init(&msg3);
+
+    /* Store Bob's g3a value for later in the protocol */
+    gcry_mpi_set(astate->g3o, msg2[3]);
+
+    /* Verify Bob's knowledge of discreet log proofs */
+    if (otrl_sm_check_know_log(msg2[1], msg2[2], astate->g1, msg2[0], 3) || 
+        otrl_sm_check_know_log(msg2[4], msg2[5], astate->g1, msg2[3], 4)) {
+        return gcry_error(GPG_ERR_INV_VALUE);
+    }
+
+    /* Combine the two halves from Bob and Alice and determine g2 and g3 */
+    gcry_mpi_powm(astate->g2, msg2[0], astate->x2, SM_MODULUS);
+    gcry_mpi_powm(astate->g3, msg2[3], astate->x3, SM_MODULUS);
+
+    /* Verify Bob's coordinate equality proof */
+    if (otrl_sm_check_equal_coords(msg2[8], msg2[9], msg2[10], msg2[6], msg2[7], astate, 5))
+        return gcry_error(GPG_ERR_INV_VALUE);
+
+    /* Calculate P and Q values for Alice */
+    gcry_mpi_t r = randomExponent();
+    gcry_mpi_t qa1 = gcry_mpi_new(SM_MOD_LEN_BITS);
+    gcry_mpi_t qa2 = gcry_mpi_new(SM_MOD_LEN_BITS);
+    gcry_mpi_powm(astate->p, astate->g3, r, SM_MODULUS);
+    gcry_mpi_set(msg3[0], astate->p);
+    gcry_mpi_powm(qa1, astate->g1, r, SM_MODULUS);
+    gcry_mpi_powm(qa2, astate->g2, astate->secret, SM_MODULUS);
+    gcry_mpi_mulm(astate->q, qa1, qa2, SM_MODULUS);
+    gcry_mpi_set(msg3[1], astate->q);
+
+    otrl_sm_proof_equal_coords(&(msg3[2]), &(msg3[3]), &(msg3[4]), astate, r, 6);
+
+    /* Calculate Ra and proof */
+    gcry_mpi_t inv = gcry_mpi_new(SM_MOD_LEN_BITS);
+    gcry_mpi_invm(inv, msg2[6], SM_MODULUS);
+    gcry_mpi_mulm(astate->pab, astate->p, inv, SM_MODULUS);
+    gcry_mpi_invm(inv, msg2[7], SM_MODULUS);
+    gcry_mpi_mulm(astate->qab, astate->q, inv, SM_MODULUS);
+    gcry_mpi_powm(msg3[5], astate->qab, astate->x3, SM_MODULUS);
+    otrl_sm_proof_equal_logs(&(msg3[6]), &(msg3[7]), astate, 7);
+
+    serialize_mpi_array(output, outputlen, SM_MSG3_LEN, msg3);
+    otrl_sm_msg_free(&msg2, SM_MSG2_LEN);
+    otrl_sm_msg_free(&msg3, SM_MSG3_LEN);
+
+    gcry_mpi_release(r);
+    gcry_mpi_release(qa1);
+    gcry_mpi_release(qa2);
+    gcry_mpi_release(inv);
+
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Create final message in SMP exchange.  Input is a message generated
+ * by otrl_sm_step3. Output is a serialized mpi array whose elements
+ * correspond to the following:
+ * [0] = rb, calculated as (Qa/Qb)^x3 where x3 is the exponent used in g3b
+ * [1] = cr, [2] = d7, Bob's ZK proof that rb is formed correctly
+ * This method also checks if Alice and Bob's secrets were the same.  If
+ * so, it returns NO_ERROR.  If the secrets differ, an INV_VALUE error is
+ * returned instead. */
+gcry_error_t otrl_sm_step4(OtrlSMBobState *bstate, const unsigned char* input, const int inputlen, unsigned char **output, int* outputlen)
+{
+    /* Read from input to find the mpis */
+    gcry_mpi_t *msg3;
+    gcry_mpi_t *msg4;
+    gcry_error_t err;
+    err = unserialize_mpi_array(&msg3, SM_MSG3_LEN, input, inputlen);
+    
+    if (err != gcry_error(GPG_ERR_NO_ERROR)) return err;
+
+    otrl_sm_msg4_init(&msg4);
+
+    if (check_group_elem(msg3[0]) || check_group_elem(msg3[1]) ||
+	    check_group_elem(msg3[5]) || check_expon(msg3[3]) ||
+	    check_expon(msg3[4]) || check_expon(msg3[7]))  {
+        return gcry_error(GPG_ERR_INV_VALUE);
+    }
+
+    /* Verify Alice's coordinate equality proof */
+    if (otrl_sm_check_equal_coords(msg3[2], msg3[3], msg3[4], msg3[0], msg3[1], bstate, 6))
+        return gcry_error(GPG_ERR_INV_VALUE);
+
+    /* Find Pa/Pb and Qa/Qb */
+    gcry_mpi_t inv = gcry_mpi_new(SM_MOD_LEN_BITS);
+    gcry_mpi_invm(inv, bstate->p, SM_MODULUS);
+    gcry_mpi_mulm(bstate->pab, msg3[0], inv, SM_MODULUS);
+    gcry_mpi_invm(inv, bstate->q, SM_MODULUS);
+    gcry_mpi_mulm(bstate->qab, msg3[1], inv, SM_MODULUS);
+
+    /* Verify Alice's log equality proof */
+    if (otrl_sm_check_equal_logs(msg3[6], msg3[7], msg3[5], bstate, 7))
+        return gcry_error(GPG_ERR_INV_VALUE);
+
+    /* Calculate Rb and proof */
+    gcry_mpi_powm(msg4[0], bstate->qab, bstate->x3, SM_MODULUS);
+    otrl_sm_proof_equal_logs(&(msg4[1]), &(msg4[2]), bstate, 8);
+
+    serialize_mpi_array(output, outputlen, SM_MSG4_LEN, msg4);
+
+    /* Calculate Rab and verify that secrets match */
+    gcry_mpi_t rab = gcry_mpi_new(SM_MOD_LEN_BITS);
+    gcry_mpi_powm(rab, msg3[5], bstate->x3, SM_MODULUS);
+    int comp = gcry_mpi_cmp(rab, bstate->pab);
+
+    /* Clean up everything allocated in this step */
+    otrl_sm_msg_free(&msg3, SM_MSG3_LEN);
+    otrl_sm_msg_free(&msg4, SM_MSG4_LEN);
+    gcry_mpi_release(rab);
+    gcry_mpi_release(inv);
+
+    if (comp)
+        return gcry_error(GPG_ERR_INV_VALUE);
+    else
+        return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Receives the final SMP message, which was generated in otrl_sm_step.
+ * This method checks if Alice and Bob's secrets were the same.  If
+ * so, it returns NO_ERROR.  If the secrets differ, an INV_VALUE error is
+ * returned instead. */
+gcry_error_t otrl_sm_step5(OtrlSMAliceState *astate, const unsigned char* input, const int inputlen)
+{
+    /* Read from input to find the mpis */
+    gcry_mpi_t *msg4;
+    gcry_error_t err;
+    err = unserialize_mpi_array(&msg4, SM_MSG4_LEN, input, inputlen);
+    
+    if (err != gcry_error(GPG_ERR_NO_ERROR)) return err;
+
+    if (check_group_elem(msg4[0]) || check_expon(msg4[2])) {
+        return gcry_error(GPG_ERR_INV_VALUE);
+    }
+
+    /* Verify Bob's log equality proof */
+    if (otrl_sm_check_equal_logs(msg4[1], msg4[2], msg4[0], astate, 8))
+        return gcry_error(GPG_ERR_INV_VALUE);
+
+    /* Calculate Rab and verify that secrets match */
+    gcry_mpi_t rab = gcry_mpi_new(SM_MOD_LEN_BITS);
+    gcry_mpi_powm(rab, msg4[0], astate->x3, SM_MODULUS);
+
+    int comp = gcry_mpi_cmp(rab, astate->pab);
+    gcry_mpi_release(rab);
+    otrl_sm_msg_free(&msg4, SM_MSG4_LEN);
+
+    if (comp)
+        return gcry_error(GPG_ERR_INV_VALUE);
+    else
+        return gcry_error(GPG_ERR_NO_ERROR);
+}
diff --git a/src/sm.h b/src/sm.h
new file mode 100644
index 0000000..10737c6
--- /dev/null
+++ b/src/sm.h
@@ -0,0 +1,74 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2.1 of the GNU Lesser General
+ *  Public License as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __SM_H__
+#define __SM_H__
+
+#include <gcrypt.h>
+
+#define SM_HASH_ALGORITHM GCRY_MD_SHA256
+#define SM_DIGEST_SIZE 32
+
+typedef enum {
+    OTRL_SMP_EXPECT1,
+    OTRL_SMP_EXPECT2,
+    OTRL_SMP_EXPECT3,
+    OTRL_SMP_EXPECT4,
+    OTRL_SMP_EXPECT5
+} NextExpectedSMP;
+
+typedef struct {
+    gcry_mpi_t secret, x2, x3, g1, g2, g3, g3o, p, q, pab, qab;
+    NextExpectedSMP nextExpected;
+} OtrlSMState;
+
+typedef OtrlSMState OtrlSMAliceState;
+typedef OtrlSMState OtrlSMBobState;
+
+/*
+ * Call this once, at plugin load time.  It sets up the modulus and
+ * generator MPIs.
+ */
+void otrl_sm_init(void);
+
+/*
+ * Initialize the fields of a SM state.
+ */
+void otrl_sm_state_new(OtrlSMState *smst);
+
+/*
+ * Initialize the fields of a SM state.  Called the first time that
+ * a user begins an SMP session.
+ */
+void otrl_sm_state_init(OtrlSMState *smst);
+
+/*
+ * Deallocate the contents of a OtrlSMState (but not the OtrlSMState
+ * itself)
+ */
+void otrl_sm_state_free(OtrlSMState *smst);
+
+gcry_error_t otrl_sm_step1(OtrlSMAliceState *astate, const unsigned char* secret, int secretlen, unsigned char** output, int* outputlen);
+gcry_error_t otrl_sm_step2a(OtrlSMBobState *bstate, const unsigned char* input, const int inputlen);
+gcry_error_t otrl_sm_step2b(OtrlSMBobState *bstate, const unsigned char* secret, int secretlen, unsigned char **output, int* outputlen);
+gcry_error_t otrl_sm_step3(OtrlSMAliceState *astate, const unsigned char* input, const int inputlen, unsigned char **output, int* outputlen);
+gcry_error_t otrl_sm_step4(OtrlSMBobState *bstate, const unsigned char* input, const int inputlen, unsigned char **output, int* outputlen);
+gcry_error_t otrl_sm_step5(OtrlSMAliceState *astate, const unsigned char* input, const int inputlen);
+
+#endif
diff --git a/src/tlv.c b/src/tlv.c
index d840ad1..efa280e 100644
--- a/src/tlv.c
+++ b/src/tlv.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
diff --git a/src/tlv.h b/src/tlv.h
index ee30370..a378b9d 100644
--- a/src/tlv.h
+++ b/src/tlv.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
@@ -35,6 +35,12 @@ typedef struct s_OtrlTLV {
 /* The sender has thrown away his OTR session keys with you */
 #define OTRL_TLV_DISCONNECTED    0x0001
 
+/* The message contains a step in the Socialist Millionaires' Protocol. */ 
+#define OTRL_TLV_SMP1            0x0002
+#define OTRL_TLV_SMP2            0x0003
+#define OTRL_TLV_SMP3            0x0004
+#define OTRL_TLV_SMP4            0x0005
+#define OTRL_TLV_SMP_ABORT       0x0006
 
 /* Make a single TLV, copying the supplied data */
 OtrlTLV *otrl_tlv_new(unsigned short type, unsigned short len,
diff --git a/src/userstate.c b/src/userstate.c
index c6f6cf3..df9e191 100644
--- a/src/userstate.c
+++ b/src/userstate.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
diff --git a/src/userstate.h b/src/userstate.h
index 2a69376..157005d 100644
--- a/src/userstate.h
+++ b/src/userstate.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
diff --git a/src/version.h b/src/version.h
index 77187c1..01b4f38 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging library
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This library is free software; you can redistribute it and/or
@@ -20,10 +20,10 @@
 #ifndef __VERSION_H__
 #define __VERSION_H__
 
-#define OTRL_VERSION "3.0.0"
+#define OTRL_VERSION "3.1.0"
 
 #define OTRL_VERSION_MAJOR 3
-#define OTRL_VERSION_MINOR 0
+#define OTRL_VERSION_MINOR 1
 #define OTRL_VERSION_SUB 0
 
 #endif
diff --git a/toolkit/ctrmode.c b/toolkit/ctrmode.c
index f2fa1a2..9c79e04 100644
--- a/toolkit/ctrmode.c
+++ b/toolkit/ctrmode.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging Toolkit
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -27,7 +27,7 @@
 /* Encrypt or decrypt data in AES-CTR mode.  (The operations are the
  * same.)  We roll our own here just to double-check that the calls
  * libotr makes to libgcrypt are doing the right thing. */
-void aes_ctr_crypt(unsigned char *out, unsigned char *in, size_t len,
+void aes_ctr_crypt(unsigned char *out, const unsigned char *in, size_t len,
 	unsigned char key[16], unsigned char ctrtop[8])
 {
     unsigned char ctr[16], encctr[16];
diff --git a/toolkit/ctrmode.h b/toolkit/ctrmode.h
index b74f98b..a7a8303 100644
--- a/toolkit/ctrmode.h
+++ b/toolkit/ctrmode.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging Toolkit
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -23,7 +23,7 @@
 /* Encrypt or decrypt data in AES-CTR mode.  (The operations are the
  * same.)  We roll our own here just to double-check that the calls
  * libotr makes to libgcrypt are doing the right thing. */
-void aes_ctr_crypt(unsigned char *out, unsigned char *in, size_t len,
+void aes_ctr_crypt(unsigned char *out, const unsigned char *in, size_t len,
 	unsigned char key[16], unsigned char ctrtop[8]);
 
 #endif
diff --git a/toolkit/otr_mackey.c b/toolkit/otr_mackey.c
index e73c94d..bb79e30 100644
--- a/toolkit/otr_mackey.c
+++ b/toolkit/otr_mackey.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging Toolkit
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
diff --git a/toolkit/otr_modify.c b/toolkit/otr_modify.c
index 776889e..2ac225c 100644
--- a/toolkit/otr_modify.c
+++ b/toolkit/otr_modify.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging Toolkit
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -75,8 +75,8 @@ int main(int argc, char **argv)
 		"length.\n");
 	usage(argv[0]);
     }
-    old_text = argv[2];
-    new_text = argv[3];
+    old_text = (const unsigned char *)argv[2];
+    new_text = (const unsigned char *)argv[3];
 
     if (sscanf(argv[4], "%u", &offset) != 1) {
 	fprintf(stderr, "Unparseable offset given.\n");
diff --git a/toolkit/otr_parse.c b/toolkit/otr_parse.c
index afdac72..348282f 100644
--- a/toolkit/otr_parse.c
+++ b/toolkit/otr_parse.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging Toolkit
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
diff --git a/toolkit/otr_readforge.c b/toolkit/otr_readforge.c
index edf4b07..1eae299 100644
--- a/toolkit/otr_readforge.c
+++ b/toolkit/otr_readforge.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging Toolkit
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -114,7 +114,8 @@ int main(int argc, char **argv)
 	    fprintf(stderr, "Out of memory!\n");
 	    exit(1);
 	}
-	aes_ctr_crypt(ciphertext, argv[2], newlen, aeskey, datamsg->ctr);
+	aes_ctr_crypt(ciphertext, (const unsigned char *)argv[2], newlen,
+		aeskey, datamsg->ctr);
 	free(datamsg->encmsg);
 	datamsg->encmsg = ciphertext;
 	datamsg->encmsglen = newlen;
diff --git a/toolkit/otr_remac.c b/toolkit/otr_remac.c
index 0ffe90b..0d4c457 100644
--- a/toolkit/otr_remac.c
+++ b/toolkit/otr_remac.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging Toolkit
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
diff --git a/toolkit/otr_sesskeys.c b/toolkit/otr_sesskeys.c
index f28fd91..656e791 100644
--- a/toolkit/otr_sesskeys.c
+++ b/toolkit/otr_sesskeys.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging Toolkit
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
diff --git a/toolkit/parse.c b/toolkit/parse.c
index e07c86a..f048530 100644
--- a/toolkit/parse.c
+++ b/toolkit/parse.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging Toolkit
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -38,7 +38,7 @@ void dump_int(FILE *stream, const char *title, unsigned int val)
 void dump_mpi(FILE *stream, const char *title, gcry_mpi_t val)
 {
     size_t plen;
-    char *d;
+    unsigned char *d;
     
     gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &plen, val);
     d = malloc(plen);
diff --git a/toolkit/parse.h b/toolkit/parse.h
index 1db65bc..171888e 100644
--- a/toolkit/parse.h
+++ b/toolkit/parse.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging Toolkit
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
diff --git a/toolkit/readotr.c b/toolkit/readotr.c
index 03927f3..f6ff00b 100644
--- a/toolkit/readotr.c
+++ b/toolkit/readotr.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging Toolkit
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
diff --git a/toolkit/readotr.h b/toolkit/readotr.h
index a0c5763..90144b8 100644
--- a/toolkit/readotr.h
+++ b/toolkit/readotr.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging Toolkit
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
diff --git a/toolkit/sesskeys.c b/toolkit/sesskeys.c
index 17acaa3..d41b77a 100644
--- a/toolkit/sesskeys.c
+++ b/toolkit/sesskeys.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging Toolkit
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
diff --git a/toolkit/sesskeys.h b/toolkit/sesskeys.h
index 7a16061..0a09e45 100644
--- a/toolkit/sesskeys.h
+++ b/toolkit/sesskeys.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging Toolkit
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
diff --git a/toolkit/sha1hmac.c b/toolkit/sha1hmac.c
index 871337e..ec54b3e 100644
--- a/toolkit/sha1hmac.c
+++ b/toolkit/sha1hmac.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging Toolkit
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
diff --git a/toolkit/sha1hmac.h b/toolkit/sha1hmac.h
index 47e57b3..ce44487 100644
--- a/toolkit/sha1hmac.h
+++ b/toolkit/sha1hmac.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging Toolkit
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/libotr.git



More information about the Pkg-privacy-commits mailing list