[Pkg-privacy-commits] [libotr] 01/225: Initial revision

Ximin Luo infinity0 at moszumanska.debian.org
Sat Aug 22 12:44:43 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 a4def9247844200b9393a48dccdcd914b9e7d6f8
Author: cypherpunk <cypherpunk>
Date:   Wed May 4 17:48:29 2005 +0000

    Initial revision
---
 AUTHORS                      |    7 +
 COPYING                      |  340 ++++++++++++++
 COPYING.LIB                  |  504 +++++++++++++++++++++
 ChangeLog                    |  252 +++++++++++
 INSTALL                      |   45 ++
 Makefile.am                  |    6 +
 NEWS                         |  200 ++++++++
 Protocol                     |  512 +++++++++++++++++++++
 README                       |  294 ++++++++++++
 configure.ac                 |   31 ++
 libotr.m4                    |  138 ++++++
 makedist                     |    7 +
 packaging/fedora/libotr.spec |  138 ++++++
 src/Makefile.am              |   13 +
 src/b64.c                    |  187 ++++++++
 src/b64.h                    |   42 ++
 src/context.c                |  267 +++++++++++
 src/context.h                |  137 ++++++
 src/dh.c                     |  245 ++++++++++
 src/dh.h                     |   91 ++++
 src/mem.c                    |  163 +++++++
 src/mem.h                    |   25 +
 src/message.c                |  958 +++++++++++++++++++++++++++++++++++++++
 src/message.h                |  191 ++++++++
 src/privkey.c                |  459 +++++++++++++++++++
 src/privkey.h                |   80 ++++
 src/proto.c                  | 1029 ++++++++++++++++++++++++++++++++++++++++++
 src/proto.h                  |  133 ++++++
 src/tlv.c                    |  107 +++++
 src/tlv.h                    |   60 +++
 src/userstate.c              |   51 +++
 src/userstate.h              |   45 ++
 src/version.h                |   29 ++
 toolkit/Makefile.am          |   44 ++
 toolkit/aes.c                |  866 +++++++++++++++++++++++++++++++++++
 toolkit/aes.h                |   26 ++
 toolkit/ctrmode.c            |   60 +++
 toolkit/ctrmode.h            |   29 ++
 toolkit/otr_mackey.c         |   65 +++
 toolkit/otr_modify.c         |  126 ++++++
 toolkit/otr_parse.c          |  129 ++++++
 toolkit/otr_readforge.c      |  132 ++++++
 toolkit/otr_remac.c          |  121 +++++
 toolkit/otr_sesskeys.c       |   92 ++++
 toolkit/otr_toolkit.1        |  109 +++++
 toolkit/parse.c              |  379 ++++++++++++++++
 toolkit/parse.h              |   90 ++++
 toolkit/readotr.c            |   91 ++++
 toolkit/readotr.h            |   29 ++
 toolkit/sesskeys.c           |   94 ++++
 toolkit/sesskeys.h           |   34 ++
 toolkit/sha1hmac.c           |   61 +++
 toolkit/sha1hmac.h           |   29 ++
 53 files changed, 9392 insertions(+)

diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..6cf447a
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,7 @@
+Off-the-Record Messaging Library and Toolkit
+
+Authors:
+
+    Nikita Borisov and Ian Goldberg <otr at cypherpunks.ca>
+
+See the README file for mailing list information
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+

+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+

+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+

+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+

+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+

+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/COPYING.LIB b/COPYING.LIB
new file mode 100644
index 0000000..b1e3f5a
--- /dev/null
+++ b/COPYING.LIB
@@ -0,0 +1,504 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+

+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+

+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+

+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+

+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+

+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+

+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+

+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+

+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+

+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    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
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..e38b42f
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,252 @@
+2005-05-03
+
+	* README:
+	* configure.ac:
+	* packaging/fedora/libotr.spec:
+	* src/version.h: Change version to 2.0.2
+
+	* packaging/debian: Remove this directory, as Thibaut VARENE
+	<varenet at debian.org> is now responsible for the debian packages.
+
+2005-02-23
+
+	* src/privkey.c (otrl_privkey_hash_to_human): Avoid writing a
+	NUL one byte past the end of the buffer
+
+2005-02-16
+
+	* README:
+	* configure.ac:
+	* packaging/debian/changelog:
+	* packaging/fedora/libotr.spec:
+	* src/version.h: Change version to 2.0.1
+
+2005-02-15
+
+	* src/message.c (otrl_message_sending, otrl_message_receiving)
+	(otrl_message_disconnect):
+	* src/proto.c (otrl_proto_accept_key_exchange)
+	(otrl_proto_create_data, otrl_proto_accept_data): Don't send
+	encrypted messages to a buddy who has disconnected his private
+	connection with us.
+
+	* src/message.c (otrl_message_sending): Don't show the user the
+	"the last message was resent" notice if the message has never
+	actually been sent before.
+
+2005-02-09
+
+	* src/proto.c (otrl_proto_create_data): Copy the msg before
+	using since, since it may be an alias for context->lastmessage,
+	which we're going to gcry_free().
+
+2005-02-08
+
+	* README:
+	* configure.ac:
+	* packaging/debian/changelog:
+	* packaging/fedora/libotr.spec:
+	* src/version.h: Change version to 2.0.0
+
+2005-02-07
+
+	* src/context.h:
+	* src/context.c (new_context, otrl_context_force_setup):
+	* src/message.c (otrl_message_sending, otrl_message_receiving):
+	* src/proto.c (otrl_proto_accept_key_exchange): Keep track of
+	whether the last message is eligible for retransmission.
+
+2005-02-02
+
+	* README:
+	* configure.ac:
+	* packaging/debian/changelog:
+	* packaging/fedora/libotr.spec:
+	* src/version.h: Change version to 1.99.0
+
+	* packaging/debian/libotr1.dirs:
+	* packaging/debian/libotr1.install:
+	* packaging/debian/rules: Build and install with the correct mandir
+
+	* packaging/debian/rules: Install a shlibs file
+
+	* packaging/debian/control: Add Replaces: to the packages so
+	that dpkg -i will install them.
+
+	* toolkit/Makefile.am: Create the mandir if it's not yet there
+
+	* packaging/debian/libotr1-dev.dirs:
+	* packaging/debian/libotr1-dev.install:
+	* packaging/fedora/libotr.spec: Package the libotr.m4 file
+
+	* Protocol: Added sections on policies and TLVs
+
+2005-02-01
+
+	* Makefile.am:
+	* src/Makefile.am:
+	* toolkit/Makefile.am: Use automake-1.8
+
+2005-01-31
+
+	* tlv.h:
+	* tlv.c:
+	* src/Makefile.am: add new files tlv.c and tlv.h
+
+	* src/message.c (otrl_message_sending): Allow you to specify a
+	TLV chain to attach to a message.
+
+	* src/message.c (otrl_message_receiving): Also return any TLV
+	chain attached to the message, if present.
+
+	* src/README: Document new TLV parameters to message functions.
+
+	* src/message.c (otrl_message_receiving): No longer handle
+	messages starting with "?OTR:" specially; that functionality now
+	goes into TLVs.
+
+	* src/message.c (otrl_message_disconnect): Send the notice of
+	disconnect as a OTRL_TLV_DISCONNECTED TLV.
+
+2005-01-30
+
+	* README: update documentation for 2.0.0 API
+
+	* src/message.c (otrl_message_receiving): Only send heartbeats
+	in response to "real" messages.
+
+	* src/message.c (otrl_message_receiving): If we receive a DATA
+	message whose *plaintext* starts with "?OTR:", display it with
+	display_otr_message if possible.
+
+	* src/message.c (otrl_message_receiving): Display OTR_ERROR
+	messages without the leading '?' using display_otr_message.
+
+	* src/message.h (otrl_message_disconnect):
+	* src/message.c (otrl_message_disconnect): new function
+
+	* src/message.c (otrl_message_receiving): Display the "received
+	unencrypted" warning message if we receive an unencrypted
+	message with policy ALWAYS, even when not CONNECTED.
+
+2005-01-29
+
+	* src/proto.c (otrl_proto_accept_key_exchange):
+	* src/message.c (otrl_message_sending, process_kem): Make the
+	retransmission of an unencrypted message in ALWAYS work.
+
+2005-01-28
+
+	* src/message.h: New callback for checking whether a given user
+	is online.
+
+	* src/message.c (otrl_message_sending): Notify the user if he
+	attempts to send an unencrypted message with policy ALWAYS.
+
+	* src/message.h: New callback for fetching OTR policy
+	* src/message.c (otrl_message_sending): Create a ConnContext if
+	we don't have one already.  Use it to fetch the OTR policy.
+	Just return if the policy is NEVER.  Only append the whitespace
+	tag if the policy is OPPORTUNISTIC or ALWAYS.  Don't send
+	unencrypted messages in ALWAYS, but store them for
+	retransmission later.
+	* src/message.c (otrl_message_receiving): Fetch the OTR policy.
+	Just return if the policy is NEVER.  Only send a Key Exchange
+	Message in response to an unexpected Data or Error Message in
+	OPPORTUNISTIC and ALWAYS.  Only recognize the whitespace tag in
+	OPPORTUNISTIC and ALWAYS.
+
+	* src/message.h:
+	* src/message.c: add accountname/protocol/username parameters to
+	notify callback
+
+	* src/message.h:
+	* src/message.c: add display_otr_message callback for displaying
+	OTR control messages
+
+2005-01-27
+	
+	* src/privkey.h: #include <gcrypt.h> since we use things from
+	libgcrypt in the .h file
+
+	* src/proto.h:
+	* src/proto.c: Make otrl_init take unsigned ints as arguments.
+
+	* src/context.h:
+	* src/context.c:
+	* src/message.c:
+	* src/proto.c: Keep track of the last message sent, and
+	potentially resend it if sending it the first time triggered a
+	rekey (because the other side had lost its OTR state, for
+	example).
+
+2005-01-26
+
+	* packaging/debian/control: Changed debian package names to
+	libotr1 and libotr1-dev.
+
+	* libotr.m4: Added copyright notice, more comments
+
+	* src/userstate.c:
+	* src/userstate.h: New files
+
+	* src/Makefile.am: Added -Wall to default CFLAGS
+	* toolkit/Makefile.am: Added -Wall to default CFLAGS
+
+	* src/context.c (otrl_context_find, otrl_context_forget_all):
+	* src/context.h (otrl_context_find, otrl_context_forget_all):
+	* src/message.c (otrl_message_sending, process_kem)
+	(process_confresp, otrl_message_receiving):
+	* src/message.h (otrl_message_sending, otrl_message_receiving)
+	(OtrlMessageAppOps.confirm_fingerprint):
+	* src/privkey.c (otrl_privkey_fingerprint, otrl_privkey_read)
+	(otrl_privkey_generate, otrl_privkey_read_fingerprints)
+	(otrl_privkey_write_fingerprints, otrl_privkey_find)
+	(otrl_privkey_forget_all):
+	* src/privkey.h (otrl_privkey_fingerprint, otrl_privkey_read)
+	(otrl_privkey_generate, otrl_privkey_read_fingerprints)
+	(otrl_privkey_write_fingerprints, otrl_privkey_find)
+	(otrl_privkey_forget_all):
+	* src/proto.c (otrl_proto_create_key_exchange)
+	(otrl_proto_accept_key_exchange):
+	* src/proto.h (otrl_proto_create_key_exchange)
+	(otrl_proto_accept_key_exchange): Added OtrlUserState parameter
+	to many calls, eliminating global state.
+
+	* src/privkey.c (otrl_privkey_fingerprint): the buffer is now
+	passed in, and not static
+
+2005-01-25
+	
+	* src/version.h: bumped version number to 2.0.0 because API
+	changed incompatibly
+	* configure.ac: bumped version number to 2.0.0 because API
+	changed incompatibly
+
+	* src/message.h: added accountname parameter to
+	confirm_fingerprint callback
+	* src/message.c: passed accountname to confirm_fingerprint
+	callback
+
+	* libotr.m4: new file
+	* Makefile.am: install (and uninstall) new libotr.m4 file
+
+	* tools/Makefile.am: clean up manpage symlinks and add an
+	uninstall rule
+
+2005-01-23
+
+	* src/proto.h: moved numeric version defines into version.h
+	* src/version.h: moved numeric version defines into version.h
+
+	* src/message.c (otrl_message_receiving): Update the context
+	list if we create a new context
+
+2005-01-22
+
+	Released 1.0.4.
+
+	Initial autoconfiscation, thanks to Greg Troxel <gdt at ir.bbn.com>.
+
+	* src/message.c: log, but otherwise ignore, unrecognized OTR
+	messages
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..9a7a433
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,45 @@
+REQUIREMENTS
+
+To compile the OTR library and toolkit, you'll need at least:
+ - libgpg-error 1.0  [ftp://ftp.gnupg.org/gcrypt/libgpg-error/]
+ - libgcrypt 1.2.0   [ftp://ftp.gnupg.org/gcrypt/libgcrypt/]
+
+If you install these with a package manager, you'll probably need the
+-dev or -devel versions of the packages.
+
+On Fedora, these packages are:
+    libgpg-error-devel libgcrypt-devel
+
+On Debian (testing or unstable), they are:
+    libgpg-error-dev libgcrypt11-dev
+
+COMPILING
+
+If you're got a CVS copy, you will need to regenerate the configure
+script using:
+
+    autoreconf -s -i
+
+Once you have the configure script (which comes with the source
+deistribution), run it with the "--with-pic" option, as well as any
+other options that may be necessary for your system.  Some examples:
+
+Linux:
+    ./configure --with-pic --prefix=/usr --mandir=/usr/share/man
+
+NETBSD:
+    CPPFLAGS="-I/usr/pkg/include" LDFLAGS="-R/usr/pkg/lib -L/usr/pkg/lib" \
+	./configure --with-pic --prefix=/usr/pkg
+
+mingw cross-compiler from Debian Linux:
+    ./configure --with-pic --build=`./config.guess` --host=i586-mingw32msvc \
+	--prefix=/usr/i586-mingw32msvc
+
+Once the configure script writes a Makefile, you should be able to just
+run "make".
+
+INSTALLATION
+
+You should be able to simply do "make install".  If you want to install
+somewhere other than / (this is useful for package creators), use
+something like "make DESTDIR=/path/to/install/to install".
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..b06e542
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,6 @@
+SUBDIRS = src toolkit
+
+EXTRA_DIST = Protocol packaging libotr.m4
+
+aclocaldir = $(datadir)/aclocal
+aclocal_DATA = libotr.m4
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..0fd4081
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,200 @@
+3 May 2005:
+- Released 2.0.2
+
+16 Feb 2005:
+- Released 2.0.1
+- Don't send encrypted messages to a buddy who has disconnected his
+  private connection with us.
+- Don't show the user the "the last message was resent" notice if the
+  message has never actually been sent before.
+- Fix a crash bug that happened when messages were retransmitted under
+  certain circumstances.
+
+8 Feb 2005:
+- Released 2.0.0
+- Keep track of whether a given message is eligible for retransmission
+
+2 Feb 2005:
+- Released 1.99.0, the first preview release of 2.0.0
+
+31 Jan 2005:
+- Machine-readable records can now be attached to Data Messages inside
+  the private channel.
+
+30 Jan 2005:
+- New OtrlUserState datatype encapsulates private keys and known
+  fingerprints, instead of having a single global list.
+- Added libotr.m4 for helping to autoconfiscate packages that use
+  libotr.
+- Resend the last message if it caused a re-keying.
+- New OtrlPolicy datatype allows you to specify a per-connection OTR
+  policy: never use OTR, OTR only if manually requested, automatically
+  start OTR if possible, refuse to *not* use OTR.
+- New callbacks: display_otr_message, policy, is_logged_in
+
+22 Jan 2005:
+- Released 1.0.4
+- Log, but otherwise ignore, unrecognized OTR messages.
+- Initial autoconfiscation, thanks to Greg Troxel <gdt at ir.bbn.com>. 
+
+18 Jan 2005:
+- Released 1.0.3
+- Split gaim-otr and libotr into separate packages.
+
+13 Jan 2005:
+- Generate private keys automatically, if needed.  Show a Please Wait
+  dialog while this is happening.
+- We may as well try to use the "tag" method of checking for OTR, even
+  when we don't already know a fingerprint for the correspondent.
+- Add version checking to the otrl_init() call.
+
+12 Jan 2005:
+- Refactored the logic parts of gaim-otr into libotr, so they can be
+  shared by other libotr-enabled apps.
+
+21 Dec 2004:
+- Released 1.0.2
+- If a Man-in-the-Middle steals both Alice's and Bob's DSA private keys,
+  he can perform a birthday attack to try to get his session id with
+  each end to match.  Since the session id was only 64 bits long, his
+  work was only 2^32, which is not enough.  We now make the session id
+  the whole SHA-1 hash, instead of truncating it.
+- Made otr_sesskeys output the calculated public key as well, for added
+  ease of forging messages when you don't know any plaintext.
+
+14 Dec 2004:
+- Released 1.0.1
+- Added a more sensible error message in the event that we receive our
+  own OTR Key Exchange messages.
+- If we're about to send a plaintext message to a correspondent for whom
+  we've got a fingerprint, append a special (whitespace) OTR tag
+  sequence.  The other side (if in fact running OTR) will recognize it
+  and start a Key Exchange.
+
+12 Dec 2004:
+- Released 1.0.0
+
+11 Dec 2004:
+- OTR button now gets sensitized and desensitized along with the other
+  buttons in the conversation window when you log in and out of
+  accounts.
+
+10 Dec 2004:
+- Released 0.9.9rc2
+- Heartbeats now only get sent if (1) we have just received a message,
+  and (2) we haven't sent one to that user in over a minute.
+
+9 Dec 2004:
+- Back out of the sending of heartbeats.  They were causing too many
+  problems.  It seems some networks don't let buddies know when you
+  log out, and then you get a dialog box "unable to send message" each
+  minute.  :-(
+
+8 Dec 2004:
+- Released 0.9.9rc1
+- Removed the 100 private connection limit, by not using a fixed amount
+  of secure memory.  Unfortuantely, this means that *no* memory is
+  pinned any more, but pinning only ever happened before in the unlikely
+  event you ran gaim as root.
+- Changed the "Private connection with (username) refreshed" dialog at
+  Paul's request so that it's no longer in "scary" "evil" bold, and
+  rephrased it so it's less likely to be misread as "refused" instead of
+  "refreshed".  ;-)
+- We now send heartbeats (OTR Data Messages with an empty message part)
+  once a minute, to anyone we're confident is still online.  If both
+  sides are doing this, then keys get rotated regularly, even if one
+  or both sides aren't actively typing.  This aids perfect forward
+  secrecy.
+
+4 Dec 2004:
+- Fixed a bug wherein multi-person chat windows would get the OTR button
+  in their button bar if the OTR plugin was enabled when one of them was
+  active.
+
+3 Dec 2004:
+- Released 0.9.1
+
+2 Dec 2004:
+- Clicking "OTR: Private" when you're already private will display an
+  info dialog letting you know the connection was refreshed (assuming it
+  actually is; if the other side isn't running OTR at all, the dialog
+  doesn't show, and if the other side had lost its private connection, a
+  new one will be established, with the "new private connection" dialog
+  displayed to each side (as before)).
+- The toolip for "OTR: Private" is now "Refresh the private connection".
+- "make install" now depends on "make all".
+- Added man page for OTR toolkit programs
+- Log a debug message when we receive and discard a heartbeat
+
+1 Dec 2004:
+- Fixed the Makefiles so that "make clean" also removes the binaries
+- Fixed the Makefiles so that they install into DESTDIR
+- Added packaging/debian
+
+30 Nov 2004:
+- Released 0.9.0
+- Included the OTR Messaging Toolkit.  See the README for details.
+
+28 Nov 2004:
+- Finished the Protocol document
+- Changed the name of the plugin binary from "otr-plugin.so" to
+  "gaim-otr.so".  *** NOTE: this means you'll have to (1) remove the
+  old otr-plugin.so file from your plugins directory, and (2) re-enable
+  the Off-the-Record Messaging plugin in the Preferences panel.
+- Included MAC keys used to create messages in the revealed MAC section
+  of the Data message, in addition to MAC keys used to verify messages.
+- Set all exported symbols to start with otrl_ (for the library) or
+  otrg_ (for the gaim plugin), in preparation for moving the pieces
+  into their own directories.
+- If we receive a Data message with no actual message in it, don't
+  display it to the user.  This may eventually be useful for doing
+  "heartbeat" key rotations.
+- Separated libotr and gaim-otr into their own directories.
+
+27 Nov 2004:
+- Switched from using gaim_notify_* to a slightly modified version that
+  doesn't grab the focus
+
+26 Nov 2004:
+- Put all the cipher operations in secure memory.  This makes each
+  private connection take 9472 bytes of secure memory, so we up the
+  available amount of secure memory to 100 times that.  Eventually,
+  we'd like to make this dynamically grow.
+
+25 Nov 2004:
+- Released 0.8.3
+- Don't put the DSA keys in libgcrypt secure memory, since (a) we read
+  them off disk anyway, and (b) we want to avoid running out of secure
+  memory.
+- Removed the "Do you want to start a private conversation" dialogs when
+  one side in encrypted and the other side isn't, and instead just try
+  to start one if we know for sure the other side supports it.
+- Sped up the DH computations by using a 320-bit exponent.
+
+23 Nov 2004:
+- Released 0.8.2
+- There was a crash if you received an OTR Query before setting up a
+  private key.  Fixed.
+- The fingerprint in the UI is now selectable, for cut/paste.
+- *** Protocol change.  We're no longer backward compatible.
+  - The "revealed MAC keys" moved out of the MAC'd region of the data
+    packet.  It's not wrong where it is, but it's more obviously
+    correct in the new place.
+
+22 Nov 2004:
+- Released 0.8.1
+- Jabber wasn't working, for two reasons:
+  - it sticks <tags>...</tags> around the message
+  - it refers to the same user by multiple names; e.g. "user at jabber.org"
+    vs. "user at jabber.org/Gaim"
+  Both are now fixed: we look for the OTR message anywhere in the packet
+  now, not just at the beginning, and we normalize all usernames.
+- Each account now has its own private key / fingerprint
+  - This is so you don't automatically leak the information that the
+    accounts are owned by the same person
+- There's a better indicator of private / not private status in the
+  conversation window, which you can click to start the private
+  communication.
+
+21 Nov 2004:
+- Initial 0.8.0 release
diff --git a/Protocol b/Protocol
new file mode 100644
index 0000000..f236882
--- /dev/null
+++ b/Protocol
@@ -0,0 +1,512 @@
+	       Off-The-Record Wire Protocol Documentation
+	       ------------------------------------------
+
+		    Nikita Borisov and Ian Goldberg
+			  <otr at cypherpunks.ca>
+
+Definitions
+-----------
+
+Data encodings:
+
+    Bytes (BYTE):
+      1 byte unsigned value
+    Shorts (SHORT):
+      2 byte unsigned value, big-endian
+    Ints (INT):
+      4 byte unsigned value, big-endian
+    Multi-precision integers (MPI):
+      4 byte unsigned len, big-endian
+      len byte unsigned value, big-endian
+      (MPIs must use the minimum-length encoding; i.e. no leading 0x00
+      bytes.  This is important when calculating public key fingerprints.)
+    Opaque variable-length data (DATA):
+      4 byte unsigned len, big-endian
+      len byte data
+    DSA signature (DSASIG):
+      (len is the length of the DSA public parameter q)
+      len byte unsigned r, big-endian
+      len byte unsigned s, big-endian
+    Initial CTR-mode counter value (CTR):
+      8 bytes data
+    Message Authentication Code (MAC):
+      20 bytes MAC data
+
+Policies:
+
+    Clients can set one of four OTR policies.  This can be done either
+    on a per-correspondent basis, or globally, or both.  The policies
+    are:
+
+    NEVER:
+	Never perform OTR with this correspondent.
+    MANUAL:
+	Only start OTR if one of you specifically requests it.
+    OPPORTUNISTIC:
+	Start OTR if there's any indication the correspondent supports it.
+    ALWAYS:
+	Only use OTR with this correspondent; it is an error to send an
+	unencrypted message to him.
+
+    The default setting should be OPPORTUNISTIC.
+
+Whitespace Tag:
+
+    There is an OTR_MESSAGE_TAG, which is a 24-byte string of whitespace
+    that clients can optionally append to (or insert in) messages to
+    unobtrusively indicate that they understand the OTR protocol.  The
+    string is as follows (in C notation):
+
+	\x20\x09\x20\x20\x09\x09\x09\x09
+	\x20\x09\x20\x09\x20\x09\x20\x20
+	\x20\x09\x20\x09\x20\x20\x09\x20
+
+    [This is the bit pattern of the string "OTR" as expressed in spaces and
+    tabs.]
+
+Protocol
+--------
+
+An OTR client maintains a separate state with each of its
+correspondents.  Initially, all correspondents start in the UNCONNECTED
+state.
+
+This is the state machine for the default (OPPORTUNISTIC) policy.  The
+modifications for other policies are below.
+
+In the UNCONNECTED state:
+
+ - It may, at the user's instigation or otherwise (for example, if the
+   correspondent's AIM Capability list indicates he supports OTR), send
+   an OTR Query message, and remain in the UNCONNECTED state.
+
+ - If it receives an OTR Query message, it replies with an OTR Key
+   Exchange message, and moves to the SETUP state.
+
+ - If it receives an OTR Key Exchange message, it:
+   - verifies the information in the Key Exchange message
+     - If the verification fails, send an OTR Error message, and remain
+       in the UNCONNECTED state.
+     - If the verification succeeds, reply with an OTR Key Exchange
+       message with the Reply field set to 0x01 (_even if_ the received
+       Key Exchange message itself had the Reply field set), inform the
+       user that private communication has been established, and move to
+       the CONNECTED state.
+
+ - If it receives an OTR Data message, it replies with an OTR Error
+   message, sends a Key Exchange message (with Reply set to 0x00), and
+   moves to the SETUP state.
+
+ - If it receives an OTR Error message, it should display the message to
+   the user, send a Key Exchange message (with Reply set to 0x00), and
+   move to the SETUP state.
+
+ - If it receives an non-OTR message:
+   - If the message contains the OTR_MESSAGE_TAG, it should reply with
+     an OTR Key Exchange message, strip the OTR_MESSAGE_TAG from the
+     message, display the message to the user, and move to the SETUP state.
+   - Otherwise, it should display the message to the user, and remain in
+     the UNCONNECTED state.
+
+In the SETUP state:
+
+ - The user, through a UI action, may elect to reset the connection
+   to the UNCONNECTED state.
+
+ - If it receives an OTR Query message, it replies with an OTR Key
+   Exchange message, and remains in the SETUP state.
+
+ - If it receives an OTR Key Exchange message, it:
+   - verifies the information in the Key Exchange message
+     - If the verification fails, send an OTR Error message, display a
+       notice of the error to the user, and remain in the SETUP state.
+     - If the verification succeeds:
+       - If the received Key Exchange message did not have the Reply field
+         set to 0x01, reply with an OTR Key Exchange (with the Reply field
+         set to 0x01).
+       - In any event,  inform the user that private communication has
+	 been established, and move to the CONNECTED state.
+
+ - If it receives an OTR Data message, it replies with an OTR Error
+   message, sends a Key Exchange message (with Reply set to 0x00), and
+   remains in the SETUP state.
+
+ - If it receives an OTR Error message, it should display the message to
+   the user, send a Key Exchange message (with Reply set to 0x00), and
+   remain in the SETUP state.
+
+ - If it receives an non-OTR message:
+   - If the message contains the OTR_MESSAGE_TAG, it should reply with
+     an OTR Key Exchange message, and strip the OTR_MESSAGE_TAG from the
+     message, display the message to the user, and remain to the SETUP state.
+   - Otherwise, it should display the message to the user, and remain
+     in the SETUP state.
+
+In the CONNECTED state:
+
+ - The user, through a UI action, may elect to reset the connection
+   to the UNCONNECTED state.
+
+ - If it receives an OTR Query message, it replies with an OTR Key
+   Exchange message, and remains in the CONNECTED state.
+
+ - If it receives an OTR Key Exchange message, it:
+   - verifies the information in the Key Exchange message
+     - If the verification fails, send an OTR Error message, display a
+       notice of the error to the user, and remain in the CONNECTED
+       state.
+     - If the verification succeeds:
+       - If the received Key Exchange message did not have the Reply field
+         set to 0x01, reply with an OTR Key Exchange (with the Reply field
+         set to 0x01).
+       - In any event, remain in the CONNECTED state.
+
+ - If it receives an OTR Data message, it:
+   - verifies the information in the Data message
+     - If the verification fails, send an OTR Error message, display a
+       notice of the error to the user, and remain in the CONNECTED
+       state.
+     - If the verification succeeds, display the (decrypted) message to
+       the user, and remain in the CONNECTED state.
+
+ - If it receives an OTR Error message, it should display the message to
+   the user, and remain in the CONNECTED state.
+
+ - If it receives an non-OTR message, it should reply with an OTR Error
+   message, and remain in the CONNECTED state.
+
+Other policies:
+
+    The above decribes what to do in the default (OPPORTUNISTIC) policy.
+
+    If the policy is NEVER: behave as if OTR is not enabled at all.
+    Pass all received messages to the user, and send all of the user's
+    messages out untouched.
+
+    If the policy is MANUAL: never send the OTR_MESSAGE_TAG in messages,
+    and never respond to one you receive.  Don't send a Key Exchange
+    Message (or change state) in response to receiving a Data or Error
+    Message in the UNCONNECTED or SETUP states.
+
+    If the policy is ALWAYS: never send an unencrypted message.  Either
+    report an error to the user, or else hold on to the message, send an
+    OTR Query Message instead, and once you enter the CONNECTED state,
+    send the original message.  Warn the user if you receive an
+    unencrypted message.
+
+Protocol messages
+-----------------
+
+There are four types of messages in the OTR protocol:
+ - OTR Query
+ - OTR Key Exchange
+ - OTR Data
+ - OTR Error
+
+OTR Query
+---------
+
+This message is sent to inquire if the correspondent supports the OTR
+protocol.
+
+Format:
+
+Any message containing the string "?OTR?" is considered an OTR Query.
+
+Since this message will be visible to the correspondent in the event
+that he does not support the OTR protocol, it may also contain
+human-readable information after this initial string.
+
+Example:
+
+?OTR?\nYour client does not support the OTR Private Messaging
+Protocol.\nSee http://www.cypherpunks.ca/otr/ for more information.
+
+OTR Key Exchange
+----------------
+
+This message is sent to inform the correspondent of your public
+signature key, and your current DH encryption key.
+
+Format:
+
+The message must contain the five bytes "?OTR:".  After that is the
+base-64 encoding of the following, followed by the byte ".":
+
+  - Protocol version (SHORT)
+    - The version number of this protocol is 0x0001.
+  - Message type (BYTE)
+    - OTR Key Exchange has message type 0x0a.
+  - Reply (BYTE)
+    - 0x01 if this Key Exchange message is being sent in reply to a Key
+      Exchange message that was just received.  0x00 otherwise.
+  - DSA p (MPI)
+  - DSA q (MPI)
+  - DSA g (MPI)
+  - DSA e (MPI)
+    - The DSA public key (p,q,g,e).
+      [The parameter 'e' is usually called 'y', but that name's taken by
+      the DH public key, below.]
+  - Sender keyid (INT)
+    - The keyid for this initial key.  Must be greater than 0.
+  - DH y (MPI)
+    - The initial DH public encryption key.  The DH group is the one
+      defined in RFC 3526 with 1536-bit modulus (hex, big-endian):
+	    FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
+	    29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
+	    EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
+	    E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
+	    EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
+	    C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
+	    83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
+	    670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF
+      and generator 2.
+  - Signature (DSASIG)
+    - A signature with the private DSA key corresponding to (p,q,g,e).
+      Take a SHA-1 hash of everything from the protocol version to the
+      end of the value of y, and sign that value.
+
+  The DSA key given in this message has a "Fingerprint", which is the
+  SHA-1 hash of the portion of the message from the beginning of the "p"
+  field (including the MPI length) to the end of the "e" field.  This
+  fingerprint should be displayed to the recipient so that he may verify
+  the sender's key.
+
+OTR Data
+--------
+
+This message is used to transmit a private message to the correspondent.
+It is also used to reveal old MAC keys.
+
+The plaintext message (either before encryption, or after decryption)
+consists of a human-readable message, optionally followed by:
+   - a single NUL (a BYTE with value 0x00)
+   AND
+   - zero or more TLV (type/length/value) records (with no padding
+     between them)
+
+Each TLV record is of the form:
+   - Type (SHORT)
+     - The type of this record.  Records with unrecognized types should
+       be ignored.
+   - Length (SHORT)
+     - The length of the following field
+   - Value (len BYTEs)  [where len is the value of the Length field]
+     - Any pertinent data for the record type.
+
+Some TLV examples:
+
+    \x00\x01\x00\x00
+       A TLV of type 1, containing no data
+
+    \x00\x00\x00\x05\x68\x65\x6c\x6c\x6f
+       A TLV of type 0, containing the value "hello"
+
+The currently defined TLV record types are:
+
+    Type 0: Padding
+    	The value may be an arbitrary amount of data, which should be
+	ignored.  This type can be used to disguise the length of the
+	plaintext message.
+
+    Type 1: Disconnected
+	If the user requests to close the private connection, you may
+	send a message (possibly with empty human-readable part)
+	containing a record with this TLV type just before you discard
+	the session keys.  If you receive a TLV record of this type,
+	you may inform the user that his correspondent has closed his
+	end of the private connection, and the user should do the same.
+
+Format:
+
+The message must contain the five bytes "?OTR:".  After that is the
+base-64 encoding of the following, followed by the byte ".":
+
+  - Protocol version (SHORT)
+    - The version number of this protocol is 0x0001.
+  - Message type (BYTE)
+    - OTR Data has message type 0x03.
+  - Sender keyid (INT)
+    - Must be strictly greater than 0, and increment by 1 with each key
+      change
+  - Recipient keyid (INT)
+    - Must therefore be strictly greater than 0, as the receiver has no
+      key with id 0
+    - The sender and recipient keyids are those used to encrypt and MAC
+      this message.
+  - DH y (MPI)
+    - The *next* [i.e. sender_keyid+1] public key for the sender
+  - Top half of counter init (CTR)
+    - This should monotonically increase (as a big-endian value) for
+      each message sent with the same (sender keyid, recipient keyid)
+      pair , and must not be all 0x00.
+  - Encrypted message (DATA)
+    - Using the appropriate encryption key (see below) derived from the
+      sender's and recipient's DH public keys (with the keyids given in
+      this message), perform AES128 counter-mode (CTR) encryption of the
+      message.  The initial counter is a 16-byte value whose first 8
+      bytes are the above "top half of counter init" value, and whose
+      last 8 bytes are all 0x00.  Note that counter mode does not change
+      the length of the message, so no message padding needs to be done.
+      If you *want* to do message padding (to disguise the length of
+      your message), pad with NULs (bytes of 0x00).  Upon receiving
+      and successfully decrypting an OTR Data Message, the decrypted
+      payload should be truncated just before the first NUL (if any).
+  - SHA1-HMAC, using the appropriate MAC key (see below) of everything
+    from the Protocol version to the end of the encrypted message (MAC)
+  - Old MAC keys to be revealed (DATA)
+    - See "Revealing MAC Keys", below.
+
+OTR Error
+---------
+
+This message is sent when a problem has occurred in the protocol.
+
+Format:
+
+Any message containing "?OTR Error:" is an OTR Error message.  The
+following part of the message should contain human-readable details of
+the error.
+
+Key Management
+--------------
+
+For each correspondent, keep track of:
+ - My two most recent DH public/private key pairs
+   - our_dh[our_keyid] (most recent) and our_dh[our_keyid-1] (previous)
+ - His two most recent DH public keys
+   - their_y[their_keyid] (most recent) and their_y[their_keyid-1] (previous)
+
+When starting a private conversation with a correspondent, generate
+two DH key pairs for yourself, and set our_keyid = 2.  Note that all DH
+key pairs should have a private part that is at least 320 bits long.
+
+When you send an OTR Key Exchange message:
+    Send the public part of our_dh[our_keyid-1], with the keyid field,
+    of course, set to (our_keyid-1).
+
+When you receive an OTR Key Exchange message:
+    If the specified keyid equals either their_keyid or their_keyid-1,
+    and the DH pubkey contained in the Key Exchange message matches the
+    one we've stored for that keyid, that's great.  Otherwise, forget
+    all values of their_y[], and of their_keyid, and set their_keyid to
+    the keyid value given in the Key Exchange message, and
+    their_y[their_keyid] to the DH pubkey value given in the Key
+    Exchange message.  their_y[their_keyid-1] should be set to NULL.
+
+    In any event, if the Reply field of the Key Exchange message was set
+    to 0x00, send a Key Exchange message with the Reply field set to
+    0x01.
+
+When you send an OTR Data message:
+    Set the sender keyid to (our_keyid-1), and the recipient keyid to
+    (their_keyid).  Set the DH pubkey in the Data message to the public
+    part of our_dh[our_keyid].  Use our_dh[our_keyid-1] and
+    their_y[their_keyid] to calculate session keys, as outlined below.
+    Use the "sending AES key" to encrypt the message, and the "sending
+    MAC key" to calculate its MAC.
+
+When you receive an OTR Data message:
+    Use the keyids in the message to select which of your DH key pairs
+    and which of his DH pubkeys to use to verify the MAC.  If the keyids
+    do not represent either the most recent key or the previous key (for
+    either the sender or receiver), reject the message.  Also reject the
+    message if the sender keyid is their_keyid-1, but
+    their_y[their_keyid-1] is NULL.
+
+    Otherwise, calculate the session keys as outlined below.  Use the
+    "receiving MAC key" to verify the MAC on the message.  If it does not
+    verify, reject the message.
+
+    Check that the counter in the Data message is strictly larger than the
+    last counter we saw using this pair of keys.  If not, reject the
+    message.
+
+    If the MAC verifies, decrypt the message using the "receiving AES key".
+
+    Finally, check if keys need rotation:
+     - If the "recipient keyid" in the Data message equals our_keyid, then
+       he's seen the public part of our most recent DH key pair, so we
+       securely forget our_dh[our_keyid-1], increment our_keyid, and set
+       our_dh[our_keyid] to a new DH key pair which we generate.
+     - If the "sender keyid" in the Data message equals their_keyid,
+       increment their_keyid, and set their_y[their_keyid] to the new DH
+       pubkey specified in the Data message.
+
+    If the message you get after decryption is of zero length, this is a
+    "heartbeat" packet.  Don't display it to the user.  (But it's still
+    useful to effect key rotations.)
+
+Calculating session keys:
+    Given one of our DH key pairs, and one of his DH pubkeys, we
+    calculate a session id, two AES keys, and two MAC keys as follows:
+
+    Let (our_x, our_y) be the private and public parts of our DH
+    key pair.  Let their_y be his DH pubkey.
+
+    First, calculate the shared secret:
+      secret = their_y ^ our_x mod DH_MODULUS
+    (^ denotes exponentiation, and DH_MODULUS is the 1536-bit DH modulus
+    from RFC 3526, as specified above).
+
+    Write the value of secret as a minimum-length MPI, as specific above
+    (4-byte big-endian len, len-byte big-endian value).  Let this
+    (4+len)-byte value be "secbytes".
+
+    Next, determine if we are the "low" end or the "high" end of this
+    key exchange.  If our_y > their_y, then we are the "high" end.
+    Otherwise, we are the "low" end.  Note that who is the "low" end and
+    who is the "high" end can change every time a new DH pubkey is
+    exchanged.
+
+    Calculate the session id as the SHA-1 hash of the (5+len)-byte value
+    composed of the byte 0x00, followed by the (4+len) bytes of
+    secbytes.  When a new private connection is established, display
+    these 8 bytes to the user as two 4-byte (big-endian) values, in C
+    "%08x" format.  If we are the "low" end of the key exchange, display
+    the first 10 bytes in bold, and the second 10 bytes in non-bold.  If
+    we are the "high" end of the key exchange, display the first 10 bytes
+    in non-bold, and the second 10 bytes in bold.  This session id can be
+    used by the parties to verify (say, over the telephone, assuming the
+    parties recognize each others' voices) that there is no
+    man-in-the-middle by having each side read his bold part to the
+    other.
+
+    Now set the values of "sendbyte" and "recvbyte" according to whether
+    we are the "low" or the "high" end of the key exchange:
+     - If we are the "high" end, set "sendbyte" to 0x01 and "recvbyte"
+       to 0x02.
+     - If we are the "low" end, set "sendbyte" to 0x02 and "recvbyte"
+       to 0x01.
+
+    Calculate the "sending AES key" to be the first 16 bytes of the
+    SHA-1 hash of the (5+len)-byte value composed of the byte
+    (sendbyte), followed by the (4+len) bytes of secbytes.
+
+    Calculate the "sending MAC key" to be all 20 bytes of the SHA-1 hash
+    of the 16-byte sending AES key.
+
+    Calculate the "receiving AES key" to be the first 16 bytes of the
+    SHA-1 hash of the (5+len)-byte value composed of the byte
+    (recvbyte), followed by the (4+len) bytes of secbytes.
+
+    Calculate the "receiving MAC key" to be all 20 bytes of the SHA-1 hash
+    of the 16-byte receiving AES key.
+
+Revealing MAC keys
+------------------
+
+Whenever you are about to forget either one of your old DH key pairs, or
+one of your correspondent's old DH pubkeys, take all of the MAC keys
+that were generated by that key (note that there are up to four: the
+sending and receiving MAC keys produced by the pairings of that key with
+each of two of the other side's keys; but note that you only need to
+take MAC keys that were actually used to either create a MAC on a
+message, or verify a MAC on a message), and put them (as a set of
+concatenated 20-byte values) into the "Old MAC keys to be revealed"
+section of the next Data message you send.  We do this to allow the
+forgeability of OTR transcripts: once the MAC keys are revealed, anyone
+can modify an OTR message and still have it appear valid.  But since we
+don't reveal the MAC keys until their corresponding pubkeys are being
+discarded, there is no danger of us accepting a message as valid which
+uses a MAC key which has already been revealed.
diff --git a/README b/README
new file mode 100644
index 0000000..0be6d4f
--- /dev/null
+++ b/README
@@ -0,0 +1,294 @@
+	      Off-the-Record Messaging Library and Toolkit
+			  v2.0.2,  3 May 2005
+
+This is a library and toolkit which implements Off-the-Record (OTR) Messaging.
+
+OTR allows you to have private conversations over IM by providing:
+ - Encryption
+   - No one else can read your instant messages.
+ - Authentication
+   - You are assured the correspondent is who you think it is.
+ - Deniability
+   - The messages you send do _not_ have digital signatures that are
+     checkable by a third party.  Anyone can forge messages after a
+     conversation to make them look like they came from you.  However,
+     _during_ a conversation, your correspondent is assured the messages
+     he sees are authentic and unmodified.
+ - Perfect forward secrecy
+   - If you lose control of your private keys, no previous conversation
+     is compromised.
+
+For more information on Off-the-Record Messaging, see
+http://www.cypherpunks.ca/otr/
+
+LIBRARY USAGE
+
+1. Initialization
+
+Before you call any other libotr routine, you need to initialize the
+library.  The easiest way to do that is to include proto.h, and use the
+macro:
+
+    OTRL_INIT;
+
+somewhere early in your program.  This should be called only once.
+
+You will also need an OtrlUserState.  An OtrlUserState encapsulates the
+list of known fingerprints and the list of private keys, so it should be
+"one per user".  Many OTR-enabled programs (such as IM clients) only have a 
+single user, so for them, you can just create a single one, and use it
+thoughout.  Create an OtrlUserState as follows:
+
+    userstate = otrl_userstate_create();
+
+If you need to free an OtrlUserState:
+
+    otrl_userstate_free(userstate);
+
+To read stored private keys:
+
+    otrl_privkey_read(userstate, privkeyfilename);
+
+To read stored fingerprints:
+
+    otrl_privkey_read_fingerprints(userstate, fingerprintfilename,
+	    add_app_info, add_app_info_data);
+
+add_app_info is a function that will be called in the event that a new
+ConnContext is created.  It will be passed the add_app_info_data that
+you supplied, as well as a pointer to the new ConnContext.  You can use
+this to add application-specific information to the ConnContext using
+the "context->app" field, for example.  If you don't need to do this,
+you can pass NULL for the last two arguments of
+otrl_privkey_read_fingerprints.
+
+2. Setting up the UI functions
+
+You need to let the library know how to do any UI it might require
+(error messages, confirming new fingerprints, etc.).  To this end, you
+need to define a number of UI functions, and collect them in a
+OtrlMessageAppOps struct.
+
+The first parameter of every UI function is "void *opdata".  This is a
+pointer you pass to the library, and it will pass back (opaquely) to the
+UI functions when it calls them.  You can use this to keep track of
+state or any other information.
+
+NOTE: if you use automatic (stack) variables for the opdata, or if you
+use heap variables that may get free()d, be extra careful in the
+confirm_fingerprint function.  You need to make sure the opdata is not
+deallocated before the user chooses whether to accept the fingerprint or
+not.
+
+You will need to include proto.h and message.h, and you can find a list
+of the UI functions in message.h.
+
+3. Sending messages
+
+When you have a message you're about to send, you'll need to know four
+things: you account name, the protocol id, the name of the recipient, and
+the message.
+
+The protocol id is just a unique string that is used to distinguish
+the user foo on AIM from the user foo on MSN, etc.  It can be anything
+you like, so long as you're consistent, but if you've got nothing better
+to use, you may as well use the ids from gaim.  (Programs that use the
+same protocol ids can share fingerprint and private key files.)  The
+gaim protocol id for AIM/ICQ is "prpl-oscar".
+
+Note that a name does not uniquely identify a user (as shown by the
+"foo" example above).  Even if you know both the name and the protocol,
+it may not identify the user, since there may be multiple "foo" users on
+IRC, on different servers.  But the *three* items (your account name,
+protocol id, their name) _must_ uniquely identify a user, so your
+account name needs to include any network identifier, such as a server
+name.  Examples would be "foo at irc.freenode.net" or "foo at jabber.org".
+Protocols such as AIM that do not have separate networks can just use
+"foo", of course.
+
+To encrypt the message (if necessary; the library keeps track of which
+users you have secure connections to, so you should *always* call this
+next function), simply do this:
+
+    gcry_error_t err;
+    char *newmessage = NULL;
+
+    err = otrl_message_sending(userstate, &ui_ops, opdata, accountname,
+	    protocolid, recipient_name, message, tlvs, &newmessage,
+	    add_app_info, add_app_info_data);
+
+add_app_info and add_app_info_data are as above, and may be NULL.
+
+tlvs should usually be NULL.  If it's not, then it points to a chain of
+OtrlTLVs which represent machine-readable data to send along with this
+message.
+
+If err is non-zero, then the library tried to encrypt the message,
+but for some reason failed.  DO NOT send the message in the clear in
+that case.
+
+If newmessage gets set by the call to something non-NULL, then you
+should replace your message with the contents of newmessage, and
+send that instead.
+
+When you're done with newmessage, you must call
+
+    otrl_message_free(newmessage)
+
+4. Receiving messages
+
+Receiving messages is similarly straightforward.  Again, you need to
+know four things: your account name, the protocol id, the sender's name,
+and the message.
+
+    int ignore_message;
+    char *newmessage = NULL;
+
+    ignore_message = otrl_message_receiving(userstate, &ui_ops, opdata,
+	    accountname, protocolid, sender_name, message, &newmessage,
+	    &tlvs, add_app_info, add_app_info_data);
+
+add_app_info and add_app_info_data are as above, and may be NULL.
+
+If otrl_message_receiving returns 1, then the message you received was
+an internal protocol message, and no message should be delivered to the
+user.
+
+If it returns 0, then check if newmessage was set to non-NULL.  If so,
+replace the received message with the contents of newmessage, and
+deliver that to the user instead.  You must call
+otrl_message_free(newmessage) when you're done with it.
+
+If otrl_message_receiving returns 0 and newmessage is NULL, then this
+was an ordinary, non-OTR message, which should just be delivered to the
+user without modification.
+
+If tlvs is set to non-NULL, then there is machine-readable data that was
+sent along with this message.  Call otrl_tlv_free(tlvs) when you're done
+dealing with it (or ignoring it).
+
+TOOLKIT
+
+Along with the library, this package comes with the OTR Messaging
+Toolkit.  This toolkit is useful for analyzing and/or forging OTR
+messages.  Why do we offer this?  Primarily, to make absolutely sure
+that transcripts of OTR conversations are really easy to forge after the
+fact.  [Note that *during* an OTR conversation, messages can't be forged
+without real-time access to the secret keys on the participants'
+computers, and in that case, all security has already been lost.]
+Easily forgeable transcripts help us provide the "Deniability" property:
+if someone claims you said something over OTR, they'll have no proof, as
+anyone at all can modify a transcript to make it say whatever they like,
+and still have all the verification come out correctly.
+
+Here are the six programs in the toolkit:
+
+ - otr_parse
+   - Parse OTR messages given on stdin, showing the values of all the
+     fields in OTR Key Exchange Messages and OTR Data Messages.
+
+ - otr_sesskeys our_privkey their_pubkey
+   - Shows our public key, the session id, two AES and two MAC keys
+     derived from the given Diffie-Hellman keys (one private, one public).
+
+ - otr_mackey aes_enc_key
+   - Shows the MAC key derived from the given AES key.
+
+ - otr_readforge aes_enc_key [newmsg]
+   - Decrypts an OTR Data message using the given AES key, and displays
+     the message, if the key was correct.
+   - If newmsg is given, replace the message with that one, encrypt
+     and MAC it properly, and output the resulting OTR Data Message.
+     This works even if the given key was not correct for the original
+     message, so as to enable complete forgeries.
+
+ - otr_modify mackey old_text new_text offset
+   - Even if you can't read the data because you don't know either
+     the AES key or the Diffie-Hellman private key, but you can make a
+     good guess that the substring "old_text" appears at the given
+     offset in the message, replace the old_text with the new_text
+     (which must be of the same length), recalculate the MAC with the
+     given mackey, and output the resulting Data message.
+   - Note that, even if you don't know any text in an existing message,
+     you can still forge messages of your choice using the otr_readforge
+     command, above.
+
+ - otr_remac mackey keyid keyid pubkey counter encdata revealed_mackeys
+   - Make a new OTR Data Message, with the given pieces (note that the
+     data part is already encrypted).  MAC it with the given mackey.
+
+NOTES
+
+Please send your bug reports, comments, suggestions, patches, etc. to us
+at the contact address below.
+
+MAILING LISTS
+
+There are three mailing lists pertaining to Off-the-Record Messaging:
+
+otr-announce:
+    http://lists.cypherpunks.ca/mailman/listinfo/otr-announce/
+    *** All users of OTR software should join this. ***  It is used to
+    announce new versions of OTR software, and other important information.
+
+otr-users:
+    http://lists.cypherpunks.ca/mailman/listinfo/otr-users/
+    Discussion of usage issues related to OTR Messaging software.
+
+otr-dev:
+    http://lists.cypherpunks.ca/mailman/listinfo/otr-dev/
+    Discussion of OTR Messaging software development.
+
+LICENSE
+
+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
+			     <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.
+
+    There is a copy of the GNU Lesser General Public License in the
+    COPYING.LIB file packaged with this library; if you cannot find it,
+    write to the Free Software Foundation, Inc., 59 Temple Place, Suite
+    330, Boston, MA 02111-1307  USA
+
+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
+		             <otr at cypherpunks.ca>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of version 2 of the GNU General Public License as
+    published by the Free Software Foundation.
+
+    This program 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 General Public License for more details.
+
+    There is a copy of the GNU General Public License in the COPYING file
+    packaged with this toolkit; if you cannot find it, write to the Free
+    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+    02111-1307  USA
+
+CONTACT
+
+To report problems, comments, suggestions, patches, etc., you can email
+the authors:
+
+Nikita Borisov and Ian Goldberg <otr at cypherpunks.ca>
+
+For more information on Off-the-Record Messaging, visit
+http://www.cypherpunks.ca/otr/
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..16dc114
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,31 @@
+dnl Process this file with autoconf to produce configure.
+
+AC_INIT(toolkit/parse.c)
+
+AM_CONFIG_HEADER(config.h)
+
+dnl Notes on version numbering:
+dnl   For an implementation-only change:
+dnl     Change the libotr package version from a.b.c to a.b.(c+1)
+dnl     Change the libotr libtool version from x:y:z to x:(y+1):z
+dnl   For a backwards-compatible API change (e.g. adding functions):
+dnl     Change the libotr package version from a.b.c to a.(b+1).0
+dnl     Change the libotr libtool version from x:y:z to (x+1):0:(z+1)
+dnl       [Note that this does *not* change the major number of the .so.]
+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, 2.0.2)
+LIBOTR_LIBTOOL_VERSION="1:2:0"
+
+AC_SUBST(LIBOTR_LIBTOOL_VERSION)
+
+AC_PROG_CC
+
+AM_PROG_LIBTOOL
+
+AM_PATH_LIBGCRYPT(1:1.2.0,,AC_MSG_ERROR(libgcrypt 1.2.0 or newer is required.))
+
+AC_OUTPUT([Makefile src/Makefile toolkit/Makefile])
+
diff --git a/libotr.m4 b/libotr.m4
new file mode 100644
index 0000000..34e9c21
--- /dev/null
+++ b/libotr.m4
@@ -0,0 +1,138 @@
+dnl
+dnl  Off-the-Record Messaging library
+dnl  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+dnl                           <otr at cypherpunks.ca>
+dnl
+dnl  This library is free software; you can redistribute it and/or
+dnl  modify it under the terms of version 2.1 of the GNU Lesser General
+dnl  Public License as published by the Free Software Foundation.
+dnl
+dnl  This library is distributed in the hope that it will be useful,
+dnl  but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+dnl  Lesser General Public License for more details.
+dnl
+dnl  You should have received a copy of the GNU Lesser General Public
+dnl  License along with this library; if not, write to the Free Software
+dnl  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+dnl
+
+dnl AM_PATH_LIBOTR([MINIMUM-VERSION [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for libotr, and define LIBOTR_CFLAGS and LIBOTR_LIBS as appropriate.
+dnl enables arguments --with-libotr-prefix=
+dnl                   --with-libotr-inc-prefix=
+dnl
+dnl You must already have found libgcrypt with AM_PATH_LIBGCRYPT
+dnl
+dnl Adapted from alsa.m4, originally by
+dnl      Richard Boulton <richard-alsa at tartarus.org>
+dnl      Christopher Lansdown <lansdoct at cs.alfred.edu>
+dnl      Jaroslav Kysela <perex at suse.cz>
+
+AC_DEFUN([AM_PATH_LIBOTR],
+[dnl Save the original CFLAGS, LDFLAGS, and LIBS
+libotr_save_CFLAGS="$CFLAGS"
+libotr_save_LDFLAGS="$LDFLAGS"
+libotr_save_LIBS="$LIBS"
+libotr_found=yes
+
+dnl
+dnl Get the cflags and libraries for libotr
+dnl
+AC_ARG_WITH(libotr-prefix,
+[  --with-libotr-prefix=PFX  Prefix where libotr is installed(optional)],
+[libotr_prefix="$withval"], [libotr_prefix=""])
+
+AC_ARG_WITH(libotr-inc-prefix,
+[  --with-libotr-inc-prefix=PFX  Prefix where libotr includes are (optional)],
+[libotr_inc_prefix="$withval"], [libotr_inc_prefix=""])
+
+dnl Add any special include directories
+AC_MSG_CHECKING(for libotr CFLAGS)
+if test "$libotr_inc_prefix" != "" ; then
+	LIBOTR_CFLAGS="$LIBOTR_CFLAGS -I$libotr_inc_prefix"
+	CFLAGS="$CFLAGS $LIBOTR_CFLAGS"
+fi
+AC_MSG_RESULT($LIBOTR_CFLAGS)
+CFLAGS="$libotr_save_CFLAGS"
+
+dnl add any special lib dirs
+AC_MSG_CHECKING(for libotr LIBS)
+if test "$libotr_prefix" != "" ; then
+	LIBOTR_LIBS="$LIBOTR_LIBS -L$libotr_prefix"
+	LDFLAGS="$LDFLAGS $LIBOTR_LIBS"
+fi
+
+dnl add the libotr library
+LIBOTR_LIBS="$LIBOTR_LIBS -lotr"
+LIBS="$LIBOTR_LIBS $LIBS"
+AC_MSG_RESULT($LIBOTR_LIBS)
+
+dnl Check for a working version of libasound that is of the right version.
+min_libotr_version=ifelse([$1], ,2.0.0,$1)
+AC_MSG_CHECKING(for libotr headers version >= $min_libotr_version)
+no_libotr=""
+    libotr_min_major_version=`echo $min_libotr_version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+    libotr_min_minor_version=`echo $min_libotr_version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+    libotr_min_sub_version=`echo $min_libotr_version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+
+AC_LANG_SAVE
+AC_LANG_C
+AC_TRY_COMPILE([
+#include <libotr/proto.h>
+#include <libotr/version.h>
+], [
+#  if(OTRL_VERSION_MAJOR > $libotr_min_major_version)
+  exit(0);
+#  else
+#    if(OTRL_VERSION_MAJOR < $libotr_min_major_version)
+#      error not present
+#    endif
+
+#    if(OTRL_VERSION_MINOR > $libotr_min_minor_version)
+  exit(0);
+#    else
+#      if(OTRL_VERSION_MINOR < $libotr_min_minor_version)
+#        error not present
+#      endif
+
+#      if(OTRL_VERSION_SUB < $libotr_min_sub_version)
+#        error not present
+#      endif
+#    endif
+#  endif
+exit(0);
+],
+  [AC_MSG_RESULT(found.)],
+  [AC_MSG_RESULT(not present.)
+   ifelse([$3], , [AC_MSG_ERROR(Sufficiently new version of libotr not found.)])
+   libotr_found=no]
+)
+AC_LANG_RESTORE
+
+dnl Now that we know that we have the right version, let's see if we have the library and not just the headers.
+AC_CHECK_LIB([otr], [otrl_message_receiving],,
+	[ifelse([$3], , [AC_MSG_ERROR(No linkable libotr was found.)])
+	 libotr_found=no],
+	 $LIBGCRYPT_LIBS
+)
+
+LDFLAGS="$libotr_save_LDFLAGS"
+LIBS="$libotr_save_LIBS"
+
+if test "x$libotr_found" = "xyes" ; then
+   ifelse([$2], , :, [$2])
+else
+   LIBOTR_CFLAGS=""
+   LIBOTR_LIBS=""
+   ifelse([$3], , :, [$3])
+fi
+
+dnl That should be it.  Now just export our symbols:
+AC_SUBST(LIBOTR_CFLAGS)
+AC_SUBST(LIBOTR_LIBS)
+])
+
diff --git a/makedist b/makedist
new file mode 100644
index 0000000..6f5b316
--- /dev/null
+++ b/makedist
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# Make the distribution tar.gz file from the CVS exported version
+
+autoreconf -s -i
+./configure --mandir=/usr/share/man --prefix=/usr --with-pic
+fakeroot make dist
diff --git a/packaging/fedora/libotr.spec b/packaging/fedora/libotr.spec
new file mode 100644
index 0000000..80a3dd1
--- /dev/null
+++ b/packaging/fedora/libotr.spec
@@ -0,0 +1,138 @@
+Summary: Off-The-Record Messaging libraray and toolkit
+Name: libotr
+%define majver 2
+%define minver 0.2
+Version: %{majver}.%{minver}
+%define debug_package %{nil}
+%define ourrelease 1
+Release: %{ourrelease}
+Source: http://www.cypherpunks.ca/otr/libotr-%{majver}.%{minver}.tar.gz
+BuildRoot: %{_tmppath}/%{name}-buildroot
+Url: http://www.cypherpunks.ca/otr/
+Vendor: Nikita Borisov and Ian Goldberg <otr at cypherpunks.ca>
+Packager: Paul Wouters <paul at cypherpunks.ca>
+License: GPL
+Group: Applications/Internet
+%define __spec_install_post /usr/lib/rpm/brp-compress || :
+
+%package toolkit
+Summary: the otr toolkit
+Group: Applications/Internet
+Provides: libotr
+Obsoletes: gaim-otr <= 1.0.2
+BuildRequires: libgcrypt-devel >= 1.2.0, libgpg-error-devel 
+Requires: libgcrypt >= 1.2.0
+Release: %{ourrelease}
+
+%package devel
+Summary: the otr library and include files
+Group: Applications/Internet
+Release: %{ourrelease}
+Requires: libotr = 2.0.2
+
+
+%description toolkit
+
+              Off-the-Record Messaging Library and Toolkit
+                          v2.0.2,  3 May 2005
+
+This is a library and toolkit which implements Off-the-Record (OTR) Messaging.
+
+OTR allows you to have private conversations over IM by providing:
+ - Encryption
+   - No one else can read your instant messages.
+ - Authentication
+   - You are assured the correspondent is who you think it is.
+ - Deniability
+   - The messages you send do _not_ have digital signatures that are
+     checkable by a third party.  Anyone can forge messages after a
+     conversation to make them look like they came from you.  However,
+     _during_ a conversation, your correspondent is assured the messages
+     he sees are authentic and unmodified.
+ - Perfect forward secrecy
+   - If you lose control of your private keys, no previous conversation
+     is compromised.
+
+For more information on Off-the-Record Messaging, see
+http://www.cypherpunks.ca/otr/
+
+
+%description devel
+              Off-the-Record Messaging Library and Toolkit
+			  v2.0.2,  3 May 2005
+
+The devel package contains the libotr library and the include files
+
+%description
+A dummy to satisfy rpm 
+
+%prep
+%setup -q -n libotr-%{majver}.%{minver}
+
+%build
+%configure --with-pic --prefix=%{_prefix} --libdir=%{_libdir} --mandir=%{_mandir}
+%{__make} \
+	CFLAGS="${RPM_OPT_FLAGS}" \
+	all
+
+%install
+rm -rf ${RPM_BUILD_ROOT}
+%{__make} \
+	DESTDIR=${RPM_BUILD_ROOT} \
+	LIBINSTDIR=%{_libdir} \
+	install
+
+%clean
+rm -rf ${RPM_BUILD_ROOT}
+
+%files 
+%defattr(-,root,root)
+%doc README COPYING COPYING.LIB Protocol
+%{_libdir}/libotr.so.*
+%{_bindir}/*
+%{_mandir}/man1/*
+
+%files devel
+%doc README COPYING.LIB 
+%{_libdir}/libotr.so
+%{_libdir}/libotr.a
+%{_libdir}/libotr.la
+%{_includedir}/libotr/*
+%{_datadir}/aclocal/*
+
+
+%changelog
+* Tue May  3 2005 Ian Goldberg <ian at cypherpunks.ca>
+- Bumped version number to 2.0.2
+* Wed Feb 16 2005 Ian Goldberg <ian at cypherpunks.ca>
+- Bumped version number to 2.0.1
+* Tue Feb  8 2005 Ian Goldberg <ian at cypherpunks.ca>
+- Bumped version number to 2.0.0
+* Wed Feb  2 2005 Ian Goldberg <ian at cypherpunks.ca>
+- Added libotr.m4 to the devel package
+- Bumped version number to 1.99.0
+* Wed Jan 19 2005 Paul Wouters <paul at cypherpunks.ca>
+- Updated spec file for the gaim-otr libotr split
+* Tue Dec 21 2004 Ian Goldberg <otr at cypherpunks.ca>
+- Bumped to version 1.0.2.
+* Fri Dec 17 2004 Paul Wouters <paul at cypherpunks.ca>
+- instll fix for x86_64
+* Sun Dec 12 2004 Ian Goldberg <otr at cypherpunks.ca>
+- Bumped to version 1.0.0.
+* Fri Dec 10 2004 Ian Goldberg <otr at cypherpunks.ca>
+- Bumped to version 0.9.9rc2. 
+* Thu Dec  9 2004 Ian Goldberg <otr at cypherpunks.ca>
+- Added CFLAGS to "make all", removed DESTDIR
+* Wed Dec  8 2004 Ian Goldberg <otr at cypherpunks.ca>
+- Bumped to version 0.9.9rc1. 
+* Fri Dec  3 2004 Ian Goldberg <otr at cypherpunks.ca>
+- Bumped to version 0.9.1. 
+* Wed Dec  1 2004 Paul Wouters <paul at cypherpunks.ca>
+- Bumped to version 0.9.0. 
+- Fixed install for tools and cos
+- Added Obsoletes: target for otr-plugin so rpm-Uhv gaim-otr removes it.
+* Mon Nov 22 2004 Ian Goldberg <otr at cypherpunks.ca>
+- Bumped version to 0.8.1
+* Sun Nov 21 2004 Paul Wouters <paul at cypherpunks.ca>
+- Initial version
+
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..04d7d05
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,13 @@
+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
+
+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
diff --git a/src/b64.c b/src/b64.c
new file mode 100644
index 0000000..d1bb487
--- /dev/null
+++ b/src/b64.c
@@ -0,0 +1,187 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <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
+ */
+
+/* Modified from: */
+
+/*********************************************************************\
+
+MODULE NAME:    b64.c
+
+AUTHOR:         Bob Trower 08/04/01
+
+LICENCE:        Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc.
+
+                Permission is hereby granted, free of charge, to any person
+                obtaining a copy of this software and associated
+                documentation files (the "Software"), to deal in the
+                Software without restriction, including without limitation
+                the rights to use, copy, modify, merge, publish, distribute,
+                sublicense, and/or sell copies of the Software, and to
+                permit persons to whom the Software is furnished to do so,
+                subject to the following conditions:
+
+                The above copyright notice and this permission notice shall
+                be included in all copies or substantial portions of the
+                Software.
+
+                THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+                KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+                WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+                PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+                OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+                OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+                OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+                SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+VERSION HISTORY:
+                Bob Trower 08/04/01 -- Create Version 0.00.00B
+
+\******************************************************************* */
+
+/* system headers */
+#include <stdlib.h>
+
+/* libotr headers */
+#include "b64.h"
+
+/*
+** Translation Table as described in RFC1113
+*/
+static const unsigned char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/*
+** Translation Table to decode (created by author)
+*/
+static const char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
+
+/*
+** encodeblock
+**
+** encode up to 3 8-bit binary bytes as 4 '6-bit' characters.
+** len must be 1, 2, or 3.
+*/
+static void encodeblock( char *out, const unsigned char *in, size_t len )
+{
+    unsigned char in0, in1, in2;
+    in0 = in[0];
+    in1 = len > 1 ? in[1] : 0;
+    in2 = len > 2 ? in[2] : 0;
+
+    out[0] = cb64[ in0 >> 2 ];
+    out[1] = cb64[ ((in0 & 0x03) << 4) | ((in1 & 0xf0) >> 4) ];
+    out[2] = len > 1 ? cb64[ ((in1 & 0x0f) << 2) | ((in2 & 0xc0) >> 6) ]
+	             : '=';
+    out[3] = len > 2 ? cb64[ in2 & 0x3f ]
+	             : '=';
+}
+
+/*
+ * base64 encode data.  Insert no linebreaks or whitespace.
+ *
+ * The buffer base64data must contain at least ((datalen+2)/3)*4 bytes of
+ * space.  This function will return the number of bytes actually used.
+ */
+size_t otrl_base64_encode(char *base64data, const unsigned char *data,
+	size_t datalen)
+{
+    size_t base64len = 0;
+
+    while(datalen > 2) {
+	encodeblock(base64data, data, 3);
+	base64data += 4;
+	base64len += 4;
+	data += 3;
+	datalen -= 3;
+    }
+    if (datalen > 0) {
+	encodeblock(base64data, data, datalen);
+	base64len += 4;
+    }
+
+    return base64len;
+}
+
+static size_t decode(unsigned char *out, const unsigned char *in,
+	size_t b64len)
+{   
+    size_t written = 0;
+    unsigned char c = 0;
+
+    if (b64len > 0) {
+	c = in[0] << 2;
+    }
+    if (b64len > 1) {
+	out[0] = c | in[1] >> 4;
+	written = 1;
+	c = in[1] << 4;
+    }
+    if (b64len > 2) {
+	out[1] = c | in[2] >> 2;
+	written = 2;
+	c = in[2] << 6;
+    }
+    if (b64len > 3) {
+	out[2] = c | in[3];
+	written = 3;
+    }
+    return written;
+}
+
+/*
+ * base64 decode data.  Skip non-base64 chars, and terminate at the
+ * first '=', or the end of the buffer.
+ *
+ * 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 base64len)
+{
+    size_t datalen = 0;
+    unsigned char b64[4];
+    size_t b64accum = 0;
+
+    while(base64len > 0) {
+	char b = *base64data;
+	unsigned char bdecode;
+	++base64data;
+	--base64len;
+	if (b < '+' || b > 'z') continue;  /* Skip non-base64 chars */
+	if (b == '=') {
+	    /* Force termination */
+	    datalen += decode(data, b64, b64accum);
+	    base64len = 0;
+	} else {
+	    bdecode = cd64[b-'+'];
+	    if (bdecode == '$') continue;  /* Skip non-base64 chars */
+	    b64[b64accum++] = bdecode-'>';
+	    if (b64accum == 4) {
+		/* We have a complete block; decode it. */
+		size_t written = decode(data, b64, b64accum);
+		data += written;
+		datalen += written;
+		b64accum = 0;
+	    }
+	}
+    }
+
+    /* Just discard any short block at the end. */
+
+    return datalen;
+}
diff --git a/src/b64.h b/src/b64.h
new file mode 100644
index 0000000..5bd4f80
--- /dev/null
+++ b/src/b64.h
@@ -0,0 +1,42 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <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 __B64_H__
+#define __B64_H__
+
+/*
+ * base64 encode data.  Insert no linebreaks or whitespace.
+ *
+ * The buffer base64data must contain at least ((datalen+2)/3)*4 bytes of
+ * space.  This function will return the number of bytes actually used.
+ */
+size_t otrl_base64_encode(char *base64data, const unsigned char *data,
+	size_t datalen);
+
+/*
+ * base64 decode data.  Skip non-base64 chars, and terminate at the
+ * first '=', or the end of the buffer.
+ *
+ * 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 base64len);
+
+#endif
diff --git a/src/context.c b/src/context.c
new file mode 100644
index 0000000..54a93b3
--- /dev/null
+++ b/src/context.c
@@ -0,0 +1,267 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <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 <assert.h>
+
+/* libgcrypt headers */
+#include <gcrypt.h>
+
+/* libotr headers */
+#include "context.h"
+
+/* Strings describing the connection states */
+const char *otrl_context_statestr[] =
+    { "Not private", "Setting up", "Private" };
+
+/* Create a new connection context. */
+static ConnContext * new_context(const char * user, const char * accountname,
+	const char * protocol)
+{
+    ConnContext * context;
+    context = malloc(sizeof(*context));
+    assert(context != NULL);
+    context->username = strdup(user);
+    context->accountname = strdup(accountname);
+    context->protocol = strdup(protocol);
+    context->state = CONN_UNCONNECTED;
+    context->fingerprint_root.fingerprint = NULL;
+    context->fingerprint_root.context = context;
+    context->fingerprint_root.next = NULL;
+    context->fingerprint_root.tous = NULL;
+    context->active_fingerprint = NULL;
+    context->their_keyid = 0;
+    context->their_y = NULL;
+    context->their_old_y = NULL;
+    context->our_keyid = 0;
+    context->our_dh_key.groupid = 0;
+    context->our_dh_key.priv = NULL;
+    context->our_dh_key.pub = NULL;
+    context->our_old_dh_key.groupid = 0;
+    context->our_old_dh_key.priv = NULL;
+    context->our_old_dh_key.pub = NULL;
+    otrl_dh_session_blank(&(context->sesskeys[0][0]));
+    otrl_dh_session_blank(&(context->sesskeys[0][1]));
+    otrl_dh_session_blank(&(context->sesskeys[1][0]));
+    otrl_dh_session_blank(&(context->sesskeys[1][1]));
+    context->numsavedkeys = 0;
+    context->saved_mac_keys = NULL;
+    context->generation = 0;
+    context->lastsent = 0;
+    context->lastmessage = NULL;
+    context->may_retransmit = 0;
+    context->otr_offer = OFFER_NOT;
+    context->app_data = NULL;
+    context->app_data_free = NULL;
+    context->next = NULL;
+    return context;
+}
+
+/* Look up a connection context by name/account/protocol from the given
+ * OtrlUserState.  If add_if_missing is true, allocate and return a new
+ * context if one does not currently exist.  In that event, call
+ * add_app_data(data, context) so that app_data and app_data_free can be
+ * filled in by the application, and set *addedp to 1. */
+ConnContext * otrl_context_find(OtrlUserState us, const char *user,
+	const char *accountname, const char *protocol, int add_if_missing,
+	int *addedp,
+	void (*add_app_data)(void *data, ConnContext *context), void *data)
+{
+    ConnContext ** curp;
+    int usercmp = 1, acctcmp = 1, protocmp = 1;
+    if (addedp) *addedp = 0;
+    if (!user || !accountname || !protocol) return NULL;
+    for (curp = &(us->context_root); *curp; curp = &((*curp)->next)) {
+        if ((usercmp = strcmp((*curp)->username, user)) > 0 ||
+		(usercmp == 0 &&
+		  (acctcmp = strcmp((*curp)->accountname, accountname)) > 0) ||
+		(usercmp == 0 && acctcmp == 0 &&
+		  (protocmp = strcmp((*curp)->protocol, protocol)) >= 0))
+	    /* We're at the right place in the list.  We've either found
+	     * it, or gone too far. */
+	    break;
+    }
+    if (usercmp == 0 && acctcmp == 0 && protocmp == 0) {
+	/* Found it! */
+	return *curp;
+    }
+    if (add_if_missing) {
+	ConnContext *newctx;
+	if (addedp) *addedp = 1;
+	newctx = new_context(user, accountname, protocol);
+	newctx->next = *curp;
+	if (*curp) {
+	    (*curp)->tous = &(newctx->next);
+	}
+	*curp = newctx;
+	newctx->tous = curp;
+	if (add_app_data) {
+	    add_app_data(data, *curp);
+	}
+	return *curp;
+    }
+    return NULL;
+}
+
+/* Find a fingerprint in a given context, perhaps adding it if not
+ * present. */
+Fingerprint *otrl_context_find_fingerprint(ConnContext *context,
+	unsigned char fingerprint[20], int add_if_missing, int *addedp)
+{
+    Fingerprint *f = context->fingerprint_root.next;
+    if (addedp) *addedp = 0;
+    while(f) {
+	if (!memcmp(f->fingerprint, fingerprint, 20)) return f;
+	f = f->next;
+    }
+    /* Didn't find it. */
+    if (add_if_missing) {
+	if (addedp) *addedp = 1;
+	f = malloc(sizeof(*f));
+	assert(f != NULL);
+	f->fingerprint = malloc(20);
+	assert(f->fingerprint != NULL);
+	memmove(f->fingerprint, fingerprint, 20);
+	f->context = context;
+	f->next = context->fingerprint_root.next;
+	if (f->next) {
+	    f->next->tous = &(f->next);
+	}
+	context->fingerprint_root.next = f;
+	f->tous = &(context->fingerprint_root.next);
+	return f;
+    }
+    return NULL;
+}
+
+/* Force a context into the CONN_SETUP state (so that it only has local
+ * DH keys). */
+void otrl_context_force_setup(ConnContext *context)
+{
+    context->state = CONN_SETUP;
+    context->active_fingerprint = NULL;
+    context->their_keyid = 0;
+    gcry_mpi_release(context->their_y);
+    context->their_y = NULL;
+    gcry_mpi_release(context->their_old_y);
+    context->their_old_y = NULL;
+    otrl_dh_session_free(&(context->sesskeys[0][0]));
+    otrl_dh_session_free(&(context->sesskeys[0][1]));
+    otrl_dh_session_free(&(context->sesskeys[1][0]));
+    otrl_dh_session_free(&(context->sesskeys[1][1]));
+    context->numsavedkeys = 0;
+    free(context->saved_mac_keys);
+    context->saved_mac_keys = NULL;
+    gcry_free(context->lastmessage);
+    context->lastmessage = NULL;
+    context->may_retransmit = 0;
+}
+
+/* Force a context into the CONN_UNCONNECTED state. */
+void otrl_context_force_disconnect(ConnContext *context)
+{
+    /* First clean up everything we'd need to do for the SETUP state */
+    otrl_context_force_setup(context);
+
+    /* Now clean up our pubkeys, too */
+    context->state = CONN_UNCONNECTED;
+    context->our_keyid = 0;
+    otrl_dh_keypair_free(&(context->our_dh_key));
+    otrl_dh_keypair_free(&(context->our_old_dh_key));
+}
+
+/* Forget a fingerprint (so long as it's not the active one.  If it's a
+ * fingerprint_root, forget the whole context (as long as
+ * and_maybe_context is set, and it's UNCONNECTED).  Also, if it's not
+ * the fingerprint_root, but it's the only fingerprint, and we're
+ * UNCONNECTED, forget the whole context if and_maybe_context is set. */
+void otrl_context_forget_fingerprint(Fingerprint *fprint,
+	int and_maybe_context)
+{
+    ConnContext *context = fprint->context;
+    if (fprint == &(context->fingerprint_root)) {
+	if (context->state == CONN_UNCONNECTED && and_maybe_context) {
+	    otrl_context_forget(context);
+	}
+    } else {
+	if (context->state != CONN_CONNECTED ||
+		context->active_fingerprint != fprint) {
+	    free(fprint->fingerprint);
+	    *(fprint->tous) = fprint->next;
+	    if (fprint->next) {
+		fprint->next->tous = fprint->tous;
+	    }
+	    free(fprint);
+	    if (context->state == CONN_UNCONNECTED &&
+		    context->fingerprint_root.next == NULL &&
+		    and_maybe_context) {
+		/* We just deleted the only fingerprint.  Forget the
+		 * whole thing. */
+		otrl_context_forget(context);
+	    }
+	}
+    }
+}
+
+/* Forget a whole context, so long as it's UNCONNECTED. */
+void otrl_context_forget(ConnContext *context)
+{
+    if (context->state != CONN_UNCONNECTED) return;
+
+    /* Just to be safe, force a disconnect.  This also frees any
+     * extraneous data lying around. */
+    otrl_context_force_disconnect(context);
+
+    /* First free all the Fingerprints */
+    while(context->fingerprint_root.next) {
+	otrl_context_forget_fingerprint(context->fingerprint_root.next, 0);
+    }
+    /* Now free all the dynamic info here */
+    free(context->username);
+    free(context->accountname);
+    free(context->protocol);
+    context->username = NULL;
+    context->accountname = NULL;
+    context->protocol = NULL;
+
+    /* Free the application data, if it exists */
+    if (context->app_data && context->app_data_free) {
+	(context->app_data_free)(context->app_data);
+	context->app_data = NULL;
+    }
+
+    /* Fix the list linkages */
+    *(context->tous) = context->next;
+    if (context->next) {
+	context->next->tous = context->tous;
+    }
+
+    free(context);
+}
+
+/* Forget all the contexts in a given OtrlUserState, forcing them to
+ * UNCONNECTED. */
+void otrl_context_forget_all(OtrlUserState us)
+{
+    while (us->context_root) {
+	otrl_context_force_disconnect(us->context_root);
+	otrl_context_forget(us->context_root);
+    }
+}
diff --git a/src/context.h b/src/context.h
new file mode 100644
index 0000000..12278f3
--- /dev/null
+++ b/src/context.h
@@ -0,0 +1,137 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <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 __CONTEXT_H__
+#define __CONTEXT_H__
+
+#include "gcrypt.h"
+#include "dh.h"
+
+typedef enum {
+    CONN_UNCONNECTED,
+    CONN_SETUP,
+    CONN_CONNECTED
+} ConnectionState;
+
+typedef struct fingerprint {
+    unsigned char *fingerprint;        /* The fingerprint, or NULL */
+    struct context *context;           /* The context to which we belong */
+    struct fingerprint *next;          /* The next fingerprint in the list */
+    struct fingerprint **tous;         /* A pointer to the pointer to us */
+} Fingerprint;
+
+typedef struct context {
+    char * username;                   /* The user this context is for */
+    char * accountname;                /* The username is relative to
+					  this account... */
+    char * protocol;                   /* ... and this protocol */
+    ConnectionState state;             /* The state of our connection to this
+					  user */
+    Fingerprint fingerprint_root;      /* The root of a linked list of
+					  Fingerprints entries */
+    Fingerprint *active_fingerprint;   /* Which fingerprint is in use now?
+                                          A pointer into the above list */
+    unsigned int their_keyid;          /* current keyid used by other side;
+                                          this is set to 0 if we get a
+					  OTRL_TLV_DISCONNECTED message from
+					  them. */
+    gcry_mpi_t their_y;                /* Y[their_keyid] (their DH pubkey) */
+    gcry_mpi_t their_old_y;            /* Y[their_keyid-1] (their prev DH
+					  pubkey) */
+    unsigned int our_keyid;            /* current keyid used by us */
+    DH_keypair our_dh_key;             /* DH key[our_keyid] */
+    DH_keypair our_old_dh_key;         /* DH key[our_keyid-1] */
+
+    DH_sesskeys sesskeys[2][2];        /* sesskeys[i][j] are the session keys
+					  derived from DH key[our_keyid-i]
+					  and mpi Y[their_keyid-j] */
+
+    /* saved mac keys to be revealed later */
+    unsigned int numsavedkeys;
+    unsigned char *saved_mac_keys;
+
+    /* generation number: increment every time we go private, and never
+     * reset to 0 (unless we remove the context entirely) */
+    unsigned int generation;
+
+    time_t lastsent;      /* The last time a Data Message was sent */
+    char *lastmessage;    /* The plaintext of the last Data Message sent */
+    int may_retransmit;   /* Is the last message eligible for
+			     retransmission? */
+
+    enum {
+	OFFER_NOT,
+	OFFER_SENT,
+	OFFER_REJECTED,
+	OFFER_ACCEPTED
+    } otr_offer;          /* Has this correspondent repsponded to our
+			     OTR offers? */
+
+    /* Application data to be associated with this context */
+    void *app_data;
+    /* A function to free the above data when we forget this context */
+    void (*app_data_free)(void *);
+
+    struct context * next;             /* Linked list pointer */
+    struct context ** tous;            /* A pointer to the pointer to us */
+} ConnContext;
+
+#include "userstate.h"
+
+/* Strings describing the connection states */
+extern const char *otrl_context_statestr[];
+
+/* Look up a connection context by name/account/protocol from the given
+ * OtrlUserState.  If add_if_missing is true, allocate and return a new
+ * context if one does not currently exist.  In that event, call
+ * add_app_data(data, context) so that app_data and app_data_free can be
+ * filled in by the application, and set *addedp to 1. */
+ConnContext * otrl_context_find(OtrlUserState us, const char *user,
+	const char *accountname, const char *protocol, int add_if_missing,
+	int *addedp,
+	void (*add_app_data)(void *data, ConnContext *context), void *data);
+
+/* Find a fingerprint in a given context, perhaps adding it if not
+ * present. */
+Fingerprint *otrl_context_find_fingerprint(ConnContext *context,
+	unsigned char fingerprint[20], int add_if_missing, int *addedp);
+
+/* Force a context into the CONN_SETUP state (so that it only has local
+ * DH keys). */
+void otrl_context_force_setup(ConnContext *context);
+
+/* Force a context into the CONN_UNCONNECTED state. */
+void otrl_context_force_disconnect(ConnContext *context);
+
+/* Forget a fingerprint (so long as it's not the active one.  If it's a
+ * fingerprint_root, forget the whole context (as long as
+ * and_maybe_context is set, and it's UNCONNECTED).  Also, if it's not
+ * the fingerprint_root, but it's the only fingerprint, and we're
+ * UNCONNECTED, forget the whole context if and_maybe_context is set. */
+void otrl_context_forget_fingerprint(Fingerprint *fprint,
+	int and_maybe_context);
+
+/* Forget a whole context, so long as it's UNCONNECTED. */
+void otrl_context_forget(ConnContext *context);
+
+/* Forget all the contexts in a given OtrlUserState, forcing them to
+ * UNCONNECTED. */
+void otrl_context_forget_all(OtrlUserState us);
+
+#endif
diff --git a/src/dh.c b/src/dh.c
new file mode 100644
index 0000000..522c29f
--- /dev/null
+++ b/src/dh.c
@@ -0,0 +1,245 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <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>
+
+/* libgcrypt headers */
+#include <gcrypt.h>
+
+/* libotr headers */
+#include "dh.h"
+
+static const char* DH1536_MODULUS_S = "0x"
+    "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+    "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+    "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+    "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+    "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+    "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+    "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+    "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF";
+static const char *DH1536_GENERATOR_S = "0x02";
+static const int DH1536_MOD_LEN_BITS = 1536;
+static const int DH1536_MOD_LEN_BYTES = 192;
+
+static gcry_mpi_t DH1536_MODULUS = NULL;
+static gcry_mpi_t DH1536_GENERATOR = NULL;
+
+/*
+ * Call this once, at plugin load time.  It sets up the modulus and
+ * generator MPIs.
+ */
+void otrl_dh_init(void)
+{
+    gcry_mpi_scan(&DH1536_MODULUS, GCRYMPI_FMT_HEX, DH1536_MODULUS_S, 0, NULL);
+    gcry_mpi_scan(&DH1536_GENERATOR, GCRYMPI_FMT_HEX, DH1536_GENERATOR_S,
+	    0, NULL);
+}
+
+/*
+ * Deallocate the contents of a DH_keypair (but not the DH_keypair
+ * itself)
+ */
+void otrl_dh_keypair_free(DH_keypair *kp)
+{
+    gcry_mpi_release(kp->priv);
+    gcry_mpi_release(kp->pub);
+    kp->priv = NULL;
+    kp->pub = NULL;
+}
+
+/*
+ * Generate a DH keypair for a specified group.
+ */ 
+gcry_error_t otrl_dh_gen_keypair(unsigned int groupid, DH_keypair *kp)
+{
+    unsigned char *secbuf = NULL;
+    gcry_mpi_t privkey = NULL;
+
+    if (groupid != DH1536_GROUP_ID) {
+	/* Invalid group id */
+	return gcry_error(GPG_ERR_INV_VALUE);
+    }
+
+    /* Generate the secret key: a random 320-bit value */
+    secbuf = gcry_random_bytes_secure(40, GCRY_STRONG_RANDOM);
+    gcry_mpi_scan(&privkey, GCRYMPI_FMT_USG, secbuf, 40, NULL);
+    gcry_free(secbuf);
+
+    kp->groupid = groupid;
+    kp->priv = privkey;
+    kp->pub = gcry_mpi_new(DH1536_MOD_LEN_BITS);
+    gcry_mpi_powm(kp->pub, DH1536_GENERATOR, privkey, DH1536_MODULUS);
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/*
+ * Construct session keys from a DH keypair and someone else's public
+ * key.
+ */
+gcry_error_t otrl_dh_session(DH_sesskeys *sess, DH_keypair *kp, gcry_mpi_t y)
+{
+    gcry_mpi_t gab;
+    size_t gablen;
+    unsigned char *gabdata;
+    unsigned char *hashdata;
+    unsigned char sendbyte, rcvbyte;
+    gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+
+    otrl_dh_session_blank(sess);
+
+    if (kp->groupid != DH1536_GROUP_ID) {
+	/* Invalid group id */
+	return gcry_error(GPG_ERR_INV_VALUE);
+    }
+
+    /* Calculate the shared secret MPI */
+    gab = gcry_mpi_new(DH1536_MOD_LEN_BITS);
+    gcry_mpi_powm(gab, y, kp->priv, DH1536_MODULUS);
+
+    /* Output it in the right format */
+    gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &gablen, gab);
+    gabdata = gcry_malloc_secure(gablen + 5);
+    if (!gabdata) {
+	gcry_mpi_release(gab);
+	return gcry_error(GPG_ERR_ENOMEM);
+    }
+    gabdata[1] = (gablen >> 24) & 0xff;
+    gabdata[2] = (gablen >> 16) & 0xff;
+    gabdata[3] = (gablen >> 8) & 0xff;
+    gabdata[4] = gablen & 0xff;
+    gcry_mpi_print(GCRYMPI_FMT_USG, gabdata+5, gablen, NULL, gab);
+    gcry_mpi_release(gab);
+
+    /* Calculate the session id */
+    hashdata = gcry_malloc_secure(20);
+    if (!hashdata) {
+	gcry_free(gabdata);
+	return gcry_error(GPG_ERR_ENOMEM);
+    }
+    gabdata[0] = 0x00;
+    gcry_md_hash_buffer(GCRY_MD_SHA1, sess->sessionid, gabdata, gablen+5);
+
+    /* Are we the "high" or "low" end of the connection? */
+    if ( gcry_mpi_cmp(kp->pub, y) > 0 ) {
+	sess->dir = SESS_DIR_HIGH;
+	sendbyte = 0x01;
+	rcvbyte = 0x02;
+    } else {
+	sess->dir = SESS_DIR_LOW;
+	sendbyte = 0x02;
+	rcvbyte = 0x01;
+    }
+
+    /* Calculate the sending encryption key */
+    gabdata[0] = sendbyte;
+    gcry_md_hash_buffer(GCRY_MD_SHA1, hashdata, gabdata, gablen+5);
+    err = gcry_cipher_open(&(sess->sendenc), GCRY_CIPHER_AES,
+	    GCRY_CIPHER_MODE_CTR, GCRY_CIPHER_SECURE);
+    if (err) goto err;
+    err = gcry_cipher_setkey(sess->sendenc, hashdata, 16);
+    if (err) goto err;
+
+    /* Calculate the sending MAC key */
+    gcry_md_hash_buffer(GCRY_MD_SHA1, sess->sendmackey, hashdata, 16);
+    err = gcry_md_open(&(sess->sendmac), GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC);
+    if (err) goto err;
+    err = gcry_md_setkey(sess->sendmac, sess->sendmackey, 20);
+    if (err) goto err;
+
+    /* Calculate the receiving encryption key */
+    gabdata[0] = rcvbyte;
+    gcry_md_hash_buffer(GCRY_MD_SHA1, hashdata, gabdata, gablen+5);
+    err = gcry_cipher_open(&(sess->rcvenc), GCRY_CIPHER_AES,
+	    GCRY_CIPHER_MODE_CTR, GCRY_CIPHER_SECURE);
+    if (err) goto err;
+    err = gcry_cipher_setkey(sess->rcvenc, hashdata, 16);
+    if (err) goto err;
+
+    /* Calculate the receiving MAC key (and save it in the DH_sesskeys
+     * struct, so we can reveal it later) */
+    gcry_md_hash_buffer(GCRY_MD_SHA1, sess->rcvmackey, hashdata, 16);
+    err = gcry_md_open(&(sess->rcvmac), GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC);
+    if (err) goto err;
+    err = gcry_md_setkey(sess->rcvmac, sess->rcvmackey, 20);
+    if (err) goto err;
+
+    gcry_free(gabdata);
+    gcry_free(hashdata);
+    return gcry_error(GPG_ERR_NO_ERROR);
+err:
+    otrl_dh_session_free(sess);
+    gcry_free(gabdata);
+    gcry_free(hashdata);
+    return err;
+}
+
+/*
+ * Deallocate the contents of a DH_sesskeys (but not the DH_sesskeys
+ * itself)
+ */
+void otrl_dh_session_free(DH_sesskeys *sess)
+{
+    gcry_cipher_close(sess->sendenc);
+    gcry_cipher_close(sess->rcvenc);
+    gcry_md_close(sess->sendmac);
+    gcry_md_close(sess->rcvmac);
+
+    otrl_dh_session_blank(sess);
+}
+
+/*
+ * Blank out the contents of a DH_sesskeys (without releasing it)
+ */
+void otrl_dh_session_blank(DH_sesskeys *sess)
+{
+    sess->sendenc = NULL;
+    sess->sendmac = NULL;
+    sess->rcvenc = NULL;
+    sess->rcvmac = NULL;
+    memset(sess->sessionid, 0, 20);
+    memset(sess->sendctr, 0, 16);
+    memset(sess->rcvctr, 0, 16);
+    memset(sess->sendmackey, 0, 20);
+    memset(sess->rcvmackey, 0, 20);
+    sess->sendmacused = 0;
+    sess->rcvmacused = 0;
+}
+
+/* Increment the top half of a counter block */
+void otrl_dh_incctr(unsigned char *ctr)
+{
+    int i;
+    for (i=8;i;--i) {
+	if (++ctr[i-1]) break;
+    }
+}
+
+/* Compare two counter values (8 bytes each).  Return 0 if ctr1 == ctr2,
+ * < 0 if ctr1 < ctr2 (as unsigned 64-bit values), > 0 if ctr1 > ctr2. */
+int otrl_dh_cmpctr(unsigned char *ctr1, unsigned char *ctr2)
+{
+    int i;
+    for (i=0;i<8;++i) {
+	int c = ctr1[i] - ctr2[i];
+	if (c) return c;
+    }
+    return 0;
+}
diff --git a/src/dh.h b/src/dh.h
new file mode 100644
index 0000000..a64c84b
--- /dev/null
+++ b/src/dh.h
@@ -0,0 +1,91 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <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 __DH_H__
+#define __DH_H__
+
+#define DH1536_GROUP_ID 5
+
+typedef struct {
+    unsigned int groupid;
+    gcry_mpi_t priv, pub;
+} DH_keypair;
+
+typedef enum {
+    SESS_DIR_LOW,
+    SESS_DIR_HIGH
+} SessionDirection;
+
+typedef struct {
+    SessionDirection dir;
+    unsigned char sessionid[20];
+    unsigned char sendctr[16];
+    unsigned char rcvctr[16];
+    gcry_cipher_hd_t sendenc;
+    gcry_cipher_hd_t rcvenc;
+    gcry_md_hd_t sendmac;
+    unsigned char sendmackey[20];
+    int sendmacused;
+    gcry_md_hd_t rcvmac;
+    unsigned char rcvmackey[20];
+    int rcvmacused;
+} DH_sesskeys;
+
+/*
+ * Call this once, at plugin load time.  It sets up the modulus and
+ * generator MPIs.
+ */
+void otrl_dh_init(void);
+
+/*
+ * Deallocate the contents of a DH_keypair (but not the DH_keypair
+ * itself)
+ */
+void otrl_dh_keypair_free(DH_keypair *kp);
+
+/*
+ * Generate a DH keypair for a specified group.
+ */ 
+gcry_error_t otrl_dh_gen_keypair(unsigned int groupid, DH_keypair *kp);
+
+/*
+ * Construct session keys from a DH keypair and someone else's public
+ * key.
+ */
+gcry_error_t otrl_dh_session(DH_sesskeys *sess, DH_keypair *kp, gcry_mpi_t y);
+
+/*
+ * Deallocate the contents of a DH_sesskeys (but not the DH_sesskeys
+ * itself)
+ */
+void otrl_dh_session_free(DH_sesskeys *sess);
+
+/*
+ * Blank out the contents of a DH_sesskeys (without releasing it)
+ */
+void otrl_dh_session_blank(DH_sesskeys *sess);
+
+/* Increment the top half of a counter block */
+void otrl_dh_incctr(unsigned char *ctr);
+
+/* Compare two counter values (8 bytes each).  Return 0 if ctr1 == ctr2,
+ * < 0 if ctr1 < ctr2 (as unsigned 64-bit values), > 0 if ctr1 > ctr2. */
+int otrl_dh_cmpctr(unsigned char *ctr1, unsigned char *ctr2);
+
+#endif
diff --git a/src/mem.c b/src/mem.c
new file mode 100644
index 0000000..574258e
--- /dev/null
+++ b/src/mem.c
@@ -0,0 +1,163 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <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
+ */
+
+/* Memory allocation routines for libgcrypt.  All of the session key
+ * information gets allocated through here, so we can wipe it out when
+ * it's free()d.  We don't use the built-in secmem functions of
+ * libgcrypt because you need to declare a fixed amount of it when you
+ * start up.
+ *
+ * Because "secure" and "insecure" allocations from libgcrypt will get
+ * handled the same way (since we're not going to be running as root,
+ * and so won't actually have pinned memory), pretend all allocated
+ * memory (but just from libgcrypt) is requested secure, and wipe it on
+ * free(). */
+
+/* Uncomment the following to add a check that our free() and realloc() only
+ * get called on things returned from our malloc(). */
+/* #define OTRL_MEM_MAGIC 0x31415926 */
+
+/* system headers */
+#ifdef OTRL_MEM_MAGIC
+#include <stdio.h>
+#endif
+#include <stdlib.h>
+
+/* libgcrypt headers */
+#include <gcrypt.h>
+
+/* libotr headers */
+#include "mem.h"
+
+static int header_size;
+
+static void *otrl_mem_malloc(size_t n)
+{
+    void *p;
+    size_t new_n = n;
+    new_n += header_size;
+
+    /* Check for overflow attack */
+    if (new_n < n) return NULL;
+    p = malloc(new_n);
+    if (p == NULL) return NULL;
+
+    ((size_t *)p)[0] = new_n;  /* Includes header size */
+#ifdef OTRL_MEM_MAGIC
+    ((size_t *)p)[1] = OTRL_MEM_MAGIC;
+#endif
+
+    return (p + header_size);
+}
+
+static int otrl_mem_is_secure(const void *p)
+{
+    return 1;
+}
+
+static void otrl_mem_free(void *p)
+{
+    void *real_p = p - header_size;
+    size_t n = ((size_t *)real_p)[0];
+#ifdef OTRL_MEM_MAGIC
+    if (((size_t *)real_p)[1] != OTRL_MEM_MAGIC) {
+	fprintf(stderr, "Illegal free!\n");
+	return;
+    }
+#endif
+
+    /* Wipe the memory (in the same way the built-in deallocator in
+     * libgcrypt would) */
+    memset(real_p, 0xff, n);
+    memset(real_p, 0xaa, n);
+    memset(real_p, 0x55, n);
+    memset(real_p, 0x00, n);
+
+    free(real_p);
+}
+
+static void *otrl_mem_realloc(void *p, size_t n)
+{
+    if (p == NULL) {
+	return otrl_mem_malloc(n);
+    } else if (n == 0) {
+	otrl_mem_free(p);
+	return NULL;
+    } else {
+	void *real_p = p - header_size;
+	void *new_p;
+	size_t old_n = ((size_t *)real_p)[0];
+#ifdef OTRL_MEM_MAGIC
+	size_t magic = ((size_t *)real_p)[1];
+#endif
+	size_t new_n = n;
+	new_n += header_size;
+
+	/* Check for overflow attack */
+	if (new_n < n) return NULL;
+
+#ifdef OTRL_MEM_MAGIC
+	if (magic != OTRL_MEM_MAGIC) {
+	    fprintf(stderr, "Illegal realloc!\n");
+	    return NULL;
+	}
+#endif
+
+	if (new_n < old_n) {
+	    /* Overwrite the space we're about to stop using */
+	    void *p = real_p + new_n;
+	    size_t excess = old_n - new_n;
+	    memset(p, 0xff, excess);
+	    memset(p, 0xaa, excess);
+	    memset(p, 0x55, excess);
+	    memset(p, 0x00, excess);
+
+	    /* We don't actually need to realloc() */
+	    new_p = real_p;
+	} else {
+	    new_p = realloc(real_p, new_n);
+	    if (new_p == NULL) return NULL;
+	}
+
+	((size_t *)new_p)[0] = new_n;  /* Includes header size */
+	return (new_p + header_size);
+    }
+}
+
+void otrl_mem_init(void)
+{
+    header_size = 8;
+#ifdef OTRL_MEM_MAGIC
+    if (header_size < 2*sizeof(size_t)) {
+	header_size = 2*sizeof(size_t);
+    }
+#else
+    if (header_size < sizeof(size_t)) {
+	header_size = sizeof(size_t);
+    }
+#endif
+
+    gcry_set_allocation_handler(
+	    otrl_mem_malloc,
+	    otrl_mem_malloc,
+	    otrl_mem_is_secure,
+	    otrl_mem_realloc,
+	    otrl_mem_free
+	);
+}
diff --git a/src/mem.h b/src/mem.h
new file mode 100644
index 0000000..f26f5fe
--- /dev/null
+++ b/src/mem.h
@@ -0,0 +1,25 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <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 __MEM_H__
+#define __MEM_H__
+
+void otrl_mem_init(void);
+
+#endif
diff --git a/src/message.c b/src/message.c
new file mode 100644
index 0000000..6179318
--- /dev/null
+++ b/src/message.c
@@ -0,0 +1,958 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <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 <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+/* libgcrypt headers */
+#include <gcrypt.h>
+
+/* libotr headers */
+#include "privkey.h"
+#include "proto.h"
+#include "message.h"
+
+/* How long after sending a packet should we wait to send a heartbeat? */
+#define HEARTBEAT_INTERVAL 60
+
+/* How old are messages allowed to be in order to be candidates for
+ * resending in response to a rekey? */
+#define RESEND_INTERVAL 60
+
+struct s_OTRConfirmResponse {
+    OTRKeyExchangeMsg kem;
+    ConnContext *context;
+};
+
+/* Deallocate a message allocated by other otrl_message_* routines. */
+void otrl_message_free(char *message)
+{
+    free(message);
+}
+
+/* Handle a message about to be sent to the network.  It is safe to pass
+ * all messages about to be sent to this routine.  add_appdata is a
+ * function that will be called in the event that a new ConnContext is
+ * created.  It will be passed the data that you supplied, as well as a
+ * pointer to the new ConnContext.  You can use this to add
+ * application-specific information to the ConnContext using the
+ * "context->app" field, for example.  If you don't need to do this, you
+ * can pass NULL for the last two arguments of otrl_message_sending.  
+ *
+ * tlvs is a chain of OtrlTLVs to append to the private message.  It is
+ * usually correct to just pass NULL here.
+ *
+ * If this routine returns non-zero, then the library tried to encrypt
+ * the message, but for some reason failed.  DO NOT send the message in
+ * the clear in that case.
+ * 
+ * If *messagep gets set by the call to something non-NULL, then you
+ * should replace your message with the contents of *messagep, and
+ * send that instead.  Call otrl_message_free(*messagep) when you're
+ * done with it. */
+gcry_error_t otrl_message_sending(OtrlUserState us, OtrlMessageAppOps
+	*ops, void *opdata, const char *accountname, const char *protocol,
+	const char *recipient, const char *message, OtrlTLV *tlvs,
+	char **messagep,
+	void (*add_appdata)(void *data, ConnContext *context),
+	void *data)
+{
+    struct context * context;
+    char * msgtosend;
+    gcry_error_t err;
+    OtrlPolicy policy = OTRL_POLICY_DEFAULT;
+    int context_added = 0;
+
+    *messagep = NULL;
+
+    if (!accountname || !protocol || !recipient || !message || !messagep)
+        return gcry_error(GPG_ERR_NO_ERROR);
+
+    /* See if we have a fingerprint for this user */
+    context = otrl_context_find(us, recipient, accountname, protocol,
+	    1, &context_added, add_appdata, data);
+
+    /* Update the context list if we added one */
+    if (context_added && ops->update_context_list) {
+	ops->update_context_list(opdata);
+    }
+
+    /* Check the policy */
+    if (ops->policy) {
+	policy = ops->policy(opdata, context);
+    }
+
+    /* Should we go on at all? */
+    if (policy == OTRL_POLICY_NEVER) {
+        return gcry_error(GPG_ERR_NO_ERROR);
+    }
+
+    /* If this is an OTR Query message, don't encrypt it. */
+    if (otrl_proto_message_type(message) == OTR_QUERY) {
+	/* Replace the "?OTR?" with a custom message */
+	char *bettermsg = otrl_proto_default_query_msg(accountname);
+	if (bettermsg) {
+	    *messagep = bettermsg;
+	}
+	return gcry_error(GPG_ERR_NO_ERROR);
+    }
+
+    if (policy == OTRL_POLICY_ALWAYS && context->state != CONN_CONNECTED) {
+	/* We're trying to send an unencrypted message with policy
+	 * ALWAYS.  Don't do that, but try to start up OTR instead. */
+	if (context->lastmessage) {
+	    gcry_free(context->lastmessage);
+	    if (ops->notify) {
+		const char *format = "You attempted to send another "
+		    "unencrypted message to %s";
+		char *primary = malloc(strlen(format) + strlen(recipient) - 1);
+		if (primary) {
+		    sprintf(primary, format, recipient);
+		    ops->notify(opdata, OTRL_NOTIFY_ERROR, accountname,
+			    protocol, recipient, "OTR Policy Violation",
+			    primary,
+			    "Unencrypted messages to this recipient are not "
+			    "allowed.  Attempting to start a private "
+			    "conversation.\n\nYour message will be "
+			    "retransmitted when the private conversation "
+			    "starts, but the previously saved message has "
+			    "been discarded.");
+		    free(primary);
+		}
+	    }
+	} else {
+	    if (ops->notify) {
+		const char *format = "You attempted to send an "
+		    "unencrypted message to %s";
+		char *primary = malloc(strlen(format) + strlen(recipient) - 1);
+		if (primary) {
+		    sprintf(primary, format, recipient);
+		    ops->notify(opdata, OTRL_NOTIFY_WARNING, accountname,
+			    protocol, recipient, "OTR Policy Violation",
+			    primary,
+			    "Unencrypted messages to this recipient are not "
+			    "allowed.  Attempting to start a private "
+			    "conversation.\n\nYour message will be "
+			    "retransmitted when the private conversation "
+			    "starts.");
+		    free(primary);
+		}
+	    }
+	}
+	context->lastmessage = gcry_malloc_secure(strlen(message) + 1);
+	if (context->lastmessage) {
+	    char *bettermsg = otrl_proto_default_query_msg(accountname);
+	    strcpy(context->lastmessage, message);
+	    context->lastsent = time(NULL);
+	    context->may_retransmit = 2;
+	    if (bettermsg) {
+		*messagep = bettermsg;
+		return gcry_error(GPG_ERR_NO_ERROR);
+	    } else {
+		return gcry_error(GPG_ERR_ENOMEM);
+	    }
+	}
+    }
+
+    if (policy == OTRL_POLICY_OPPORTUNISTIC || policy == OTRL_POLICY_ALWAYS) {
+	if (context->state == CONN_UNCONNECTED &&
+		context->otr_offer != OFFER_REJECTED) {
+	    /* See if this user can speak OTR.  Append the OTR_MESSAGE_TAG
+	     * to the plaintext message, and see if he responds. */
+	    size_t msglen = strlen(message);
+	    char *taggedmsg = malloc(msglen + strlen(OTR_MESSAGE_TAG) + 1);
+	    if (taggedmsg) {
+		strcpy(taggedmsg, message);
+		strcpy(taggedmsg + msglen, OTR_MESSAGE_TAG);
+		*messagep = taggedmsg;
+		if (context) {
+		    context->otr_offer = OFFER_SENT;
+		}
+	    }
+	}
+    }
+
+    /* If we're not going to encrypt anything, just return here. */
+    if (context->state != CONN_CONNECTED) {
+        return gcry_error(GPG_ERR_NO_ERROR);
+    }
+
+    /* If the other side has disconnected, inform the user and don't
+     * send the message. */
+    if (context->their_keyid == 0) {
+	*messagep = strdup("");
+	if (ops->notify) {
+	    const char *fmt = "%s has already closed his private connection "
+		"to you";
+	    char *primary = malloc(strlen(fmt) + strlen(recipient) - 1);
+	    if (primary) {
+		sprintf(primary, fmt, recipient);
+		ops->notify(opdata, OTRL_NOTIFY_ERROR, 
+			accountname, protocol, recipient,
+			"Private connection closed", primary,
+			"Your message was not sent.  Either close your "
+			"private connection to him, or refresh it.");
+	    }
+	}
+	if (!(*messagep)) {
+	    return gcry_error(GPG_ERR_ENOMEM);
+	}
+        return gcry_error(GPG_ERR_NO_ERROR);
+    }
+
+    /* Create the new, encrypted message */
+    err = otrl_proto_create_data(&msgtosend, context, message, tlvs);
+    if (!err) {
+	context->lastsent = time(NULL);
+	*messagep = msgtosend;
+    } else {
+	/* Uh, oh.  Whatever we do, *don't* send the message in the
+	 * clear. */
+	*messagep = strdup("?OTR Error: Error occurred encrypting message");
+	if (ops->notify) {
+	    ops->notify(opdata, OTRL_NOTIFY_ERROR, 
+		    accountname, protocol, recipient,
+		    "Error encrypting message",
+		    "An error occurred when encrypting your message",
+		    "The message was not sent.");
+	}
+	if (!(*messagep)) {
+	    return gcry_error(GPG_ERR_ENOMEM);
+	}
+    }
+    return err;
+}
+
+/* If err == 0, send the message to the given user.  Otherwise, display
+ * an appripriate error dialog.  Return the value of err that was
+ * passed. */
+static gcry_error_t send_or_error(OtrlMessageAppOps *ops, void *opdata,
+	gcry_error_t err, const char *accountname, const char *protocol,
+	const char *who, const char *msg)
+{
+    if (!err) {
+	if (msg && *msg) {
+	    if (ops->inject_message) {
+		ops->inject_message(opdata, accountname, protocol, who, msg);
+	    }
+	}
+    } else {
+	const char *buf_format = "Error creating OTR Key "
+		"Exchange Message: %s";
+	const char *strerr = gcry_strerror(err);
+	char *buf = malloc(strlen(buf_format) + strlen(strerr) - 1);
+	if (buf) {
+	    sprintf(buf, buf_format, strerr);
+	}
+	if (ops->notify) {
+	    ops->notify(opdata, OTRL_NOTIFY_ERROR, accountname, protocol,
+		    who, "OTR error", buf, NULL);
+	}
+	free(buf);
+    }
+    return err;
+}
+
+/* Return 1 if this Key Exchange Message caused us to rekey; that is,
+ * either we were in CONN_CONNECTED, and we're again in CONN_CONNECTED,
+ * but with new keys, or else we weren't in CONN_CONNECTED before and
+ * now we are. */
+static int process_kem(OtrlUserState us, OtrlMessageAppOps *ops, void *opdata,
+	ConnContext *context, Fingerprint *fprint, OTRKeyExchangeMsg kem)
+{
+    gcry_error_t err;
+    char *msgtosend;
+    ConnectionState state = context->state;
+    unsigned int generation = context->generation;
+    int retval = 0;
+
+    if (fprint == NULL) {
+	/* We now need to add this fingerprint */
+	int added = 0;
+	fprint = otrl_context_find_fingerprint(context, kem->key_fingerprint,
+		1, &added);
+	if (added) {
+	    /* This may not be the case, in the event that multiple
+	     * dialogs for the same fingerprint are open at the same
+	     * time. */
+	    if (ops->write_fingerprints) {
+		ops->write_fingerprints(opdata);
+	    }
+	}
+    }
+    
+    /* OK, we've received a Key Exchange Message, with a known
+     * fingerprint. */
+    err = otrl_proto_accept_key_exchange(us, context, fprint, kem, &msgtosend,
+	    ops->create_privkey, opdata);
+    send_or_error(ops, opdata, err, context->accountname, context->protocol,
+	    context->username, msgtosend);
+    free(msgtosend);
+    if (ops->update_context_list) {
+	ops->update_context_list(opdata);
+    }
+
+    /* See if we need to inform the user of a change to a secure state */
+    if ((state != CONN_CONNECTED && context->state == CONN_CONNECTED)
+	    || generation != context->generation) {
+	if (ops->gone_secure) {
+	    ops->gone_secure(opdata, context);
+	}
+	retval = 1;
+    }
+
+    /* See if we need to inform the user of a change out of a secure
+     * state.  (This should only happen if we're in the CONNECTED state,
+     * the correspondent has disconnected (and lost session state), we
+     * receive a new Key Exchange message from him, but there's some
+     * sort of error when setting up our new connection state. */
+    if (state == CONN_CONNECTED && context->state != CONN_CONNECTED) {
+	if (ops->gone_insecure) {
+	    ops->gone_insecure(opdata, context);
+	}
+    }
+
+    /* See if we need to inform the user that a Key Exchange has been
+     * received, but it's just for the (unchanged) old connection. */
+    if (state == CONN_CONNECTED && context->state == CONN_CONNECTED &&
+	    generation == context->generation) {
+	if (ops->still_secure) {
+	    ops->still_secure(opdata, context, kem->is_reply);
+	}
+    }
+
+    return retval;
+}
+
+static void process_confresp(OtrlUserState us, OtrlMessageAppOps *ops,
+	void *opdata, OTRConfirmResponse *confresp, int resp)
+{
+    if (resp == 1) {
+	process_kem(us, ops, opdata, confresp->context, NULL, confresp->kem);
+    }
+    otrl_proto_free_key_exchange(confresp->kem);
+    free(confresp);
+}
+
+/* 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.
+ * It will be passed the data that you supplied, as well as
+ * a pointer to the new ConnContext.  You can use this to add
+ * application-specific information to the ConnContext using the
+ * "context->app" field, for example.  If you don't need to do this, you
+ * can pass NULL for the last two arguments of otrl_message_receiving.  
+ *
+ * If otrl_message_receiving returns 1, then the message you received
+ * was an internal protocol message, and no message should be delivered
+ * to the user.
+ *
+ * If it returns 0, then check if *messagep was set to non-NULL.  If
+ * so, replace the received message with the contents of *messagep, and
+ * deliver that to the user instead.  You must call
+ * otrl_message_free(*messagep) when you're done with it.  If tlvsp is
+ * non-NULL, *tlvsp will be set to a chain of any TLVs that were
+ * transmitted along with this message.  You must call
+ * otrl_tlv_free(*tlvsp) when you're done with those.
+ *
+ * If otrl_message_receiving returns 0 and *messagep is NULL, then this
+ * was an ordinary, non-OTR message, which should just be delivered to
+ * the user without modification. */
+int otrl_message_receiving(OtrlUserState us, OtrlMessageAppOps *ops,
+	void *opdata, const char *accountname, const char *protocol,
+	const char *sender, const char *message, char **newmessagep,
+	OtrlTLV **tlvsp,
+	void (*add_appdata)(void *data, ConnContext *context),
+	void *data)
+{
+    ConnContext * context;
+    OTRMessageType msgtype;
+    int context_added = 0;
+    ConnectionState state;
+    OtrlPolicy policy = OTRL_POLICY_DEFAULT;
+    int ignore_message = -1;
+
+    if (!accountname || !protocol || !sender || !message || !newmessagep)
+        return 0;
+
+    *newmessagep = NULL;
+    if (tlvsp) *tlvsp = NULL;
+
+    /* Find our context and state with this correspondent */
+    context = otrl_context_find(us, sender, accountname,
+	    protocol, 1, &context_added, add_appdata, data);
+
+    /* Update the context list if we added one */
+    if (context_added && ops->update_context_list) {
+	ops->update_context_list(opdata);
+    }
+
+    /* Check the policy */
+    if (ops->policy) {
+	policy = ops->policy(opdata, context);
+    }
+
+    /* Should we go on at all? */
+    if (policy == OTRL_POLICY_NEVER) {
+        return 0;
+    }
+
+    /* What type of message is it?  Note that this just checks the
+     * header; it's not necessarily a _valid_ message of this type. */
+    msgtype = otrl_proto_message_type(message);
+    state = context->state;
+
+    /* See if they responded to our OTR offer */
+    if (policy == OTRL_POLICY_OPPORTUNISTIC || policy == OTRL_POLICY_ALWAYS) {
+	if (msgtype != OTR_NOTOTR) {
+	    context->otr_offer = OFFER_ACCEPTED;
+	} else if (context->otr_offer == OFFER_SENT) {
+	    context->otr_offer = OFFER_REJECTED;
+	}
+    }
+
+    switch(msgtype) {
+	char *tag;
+	case OTR_QUERY:
+	    switch(state) {
+		char *msgtosend;
+		gcry_error_t err;
+		case CONN_UNCONNECTED:
+		case CONN_SETUP:
+		    err = otrl_proto_create_key_exchange(us, &msgtosend,
+			    context, 0, ops->create_privkey, opdata);
+
+		    if (!send_or_error(ops, opdata, err, accountname,
+				protocol, sender, msgtosend)) {
+			context->state = CONN_SETUP;
+			if (ops->update_context_list) {
+			    ops->update_context_list(opdata);
+			}
+		    }
+		    free(msgtosend);
+		    break;
+		case CONN_CONNECTED:
+		    /* Just reply with a Key Exchange message, but stay
+		     * in the CONNECTED state. */
+		    err = otrl_proto_create_key_exchange(us, &msgtosend,
+			    context, 0, ops->create_privkey, opdata);
+		    send_or_error(ops, opdata, err, accountname, protocol,
+			    sender, msgtosend);
+		    free(msgtosend);
+		    break;
+	    }
+	    /* Don't display the Query message to the user. */
+	    if (ignore_message == -1) ignore_message = 1;
+	    break;
+	case OTR_KEYEXCH:
+	    switch(state) {
+		gcry_error_t err;
+		OTRKeyExchangeMsg kem;
+		Fingerprint *found_print;
+		case CONN_UNCONNECTED:
+		case CONN_SETUP:
+		case CONN_CONNECTED:
+		    /* Even if we're currently CONNECTED, receiving a
+		     * Key Exchange message means the other side has
+		     * lost the connection for some reason.  (Or else
+		     * that we sent them an explicit OTR Query message.)
+		     * Just accept the message as usual (with all the
+		     * fingerprint checks, and
+		     * otrl_proto_accept_key_exchange() will deal with
+		     * keeping the state consistent. */
+		    err = otrl_proto_parse_key_exchange(&kem, message);
+		    found_print = NULL;
+
+		    if (err) {
+			const char *buf_format = "We received a malformed "
+				"Key Exchange message from %s.";
+			char *buf = malloc(strlen(buf_format) + strlen(sender)
+				- 1);
+			if (buf) {
+			    sprintf(buf, buf_format, sender);
+			}
+			if (ops->notify) {
+			    ops->notify(opdata, OTRL_NOTIFY_ERROR,
+				    accountname, protocol, sender,
+				    "OTR Error", buf, NULL);
+			}
+			free(buf);
+			ignore_message = 1;
+			break;
+		    }
+
+		    /* See if we're talking to ourselves */
+		    if ((context->state == CONN_SETUP ||
+				context->state == CONN_CONNECTED) &&
+			    (!gcry_mpi_cmp(kem->dh_pubkey,
+					   context->our_old_dh_key.pub)))
+		    {
+			/* Yes, we are. */
+			if (ops->notify) {
+			    ops->notify(opdata, OTRL_NOTIFY_ERROR,
+				accountname, protocol, sender, "OTR Error",
+				"We are receiving our own OTR messages.",
+				"You are either trying to talk to yourself, "
+				"or someone is reflecting your messages back "
+				"at you.");
+			}
+			ignore_message = 1;
+			break;
+		    }
+
+		    found_print = otrl_context_find_fingerprint(context,
+			    kem->key_fingerprint, 0, NULL);
+
+		    if (!found_print) {
+			/* Warn the user about the new fingerprint */
+			OTRConfirmResponse *confresp =
+			    malloc(sizeof(OTRConfirmResponse));
+			if (confresp) {
+			    confresp->kem = kem;
+			    confresp->context = context;
+			    if (ops->confirm_fingerprint) {
+				ops->confirm_fingerprint(us, opdata,
+					accountname, protocol, sender, kem,
+					process_confresp, confresp);
+			    } else {
+				process_confresp(us, ops, opdata, confresp,
+					-1);
+			    }
+			} else {
+			    otrl_proto_free_key_exchange(kem);
+			}
+		    } else {
+			time_t now;
+			int rekeyed = process_kem(us, ops, opdata,
+				context, found_print, kem);
+			otrl_proto_free_key_exchange(kem);
+
+			/* If we just rekeyed in response to a Key
+			 * Exchange Message, see if there's a message
+			 * we sent recently that should be resent. */
+			if (rekeyed) now = time(NULL);
+			if (rekeyed && context->lastmessage != NULL &&
+				context->their_keyid > 0 &&
+				context->may_retransmit &&
+				context->lastsent >= (now - RESEND_INTERVAL)) {
+			    char *resendmsg;
+			    int resending = (context->may_retransmit == 1);
+
+			    /* Re-encrypt the message with the new keys */
+			    err = otrl_proto_create_data(&resendmsg,
+				    context, context->lastmessage, NULL);
+			    if (!err) {
+				const char *format = "<b>The last message "
+				    "to %s was resent.</b>";
+				char *buf;
+
+				/* Resend the message */
+				if (ops->inject_message) {
+				    ops->inject_message(opdata, accountname,
+					    protocol, sender, resendmsg);
+				}
+				free(resendmsg);
+				context->lastsent = now;
+
+				if (!resending) {
+				    /* We're actually just sending it
+				     * for the first time. */
+				    ignore_message = 1;
+				} else {
+				    /* Let the user know we resent it */
+				    buf = malloc(strlen(format) +
+					    strlen(context->username) - 1);
+				    if (buf) {
+					sprintf(buf, format,
+						context->username);
+					if (ops->display_otr_message) {
+					    if (!ops->display_otr_message(
+							opdata, accountname,
+							protocol, sender,
+							buf)) {
+						ignore_message = 1;
+					    }
+					}
+					if (ignore_message != 1) {
+					    *newmessagep = buf;
+					    ignore_message = 0;
+					} else {
+					    free(buf);
+					}
+				    }
+				}
+			    }
+			}
+		    }
+		    break;
+	    }
+	    /* Don't deliver the Key Exchange message to the user */
+	    if (ignore_message == -1) ignore_message = 1;
+	    break;
+	case OTR_DATA:
+	    switch(state) {
+		gcry_error_t err;
+		OtrlTLV *tlvs;
+		char *plaintext;
+		char *buf;
+		char *format;
+		char *msgtosend;
+		case CONN_UNCONNECTED:
+		case CONN_SETUP:
+		    /* Don't use g_strdup_printf here, because someone
+		     * (not us) is going to free() the *newmessagep pointer,
+		     * not g_free() it. */
+		    format = "<b>The encrypted message received from %s is "
+			"unreadable, as you are not currently communicating "
+			"privately.</b>";
+		    buf = malloc(strlen(format) + strlen(context->username)
+			    - 1);  /* Remove "%s", add username + '\0' */
+		    if (buf) {
+			sprintf(buf, format, context->username);
+			if (ops->display_otr_message) {
+			    if (!ops->display_otr_message(opdata, accountname,
+					protocol, sender, buf)) {
+				ignore_message = 1;
+			    }
+			}
+			if (ignore_message != 1) {
+			    *newmessagep = buf;
+			    ignore_message = 0;
+			} else {
+			    free(buf);
+			}
+		    }
+		    format = "?OTR Error: You sent encrypted "
+			    "data to %s, who wasn't expecting it.";
+		    buf = malloc(strlen(format) + strlen(context->accountname)
+			    - 1);
+		    if (buf) {
+			sprintf(buf, format, context->accountname);
+			if (ops->inject_message) {
+			    ops->inject_message(opdata, accountname, protocol,
+				    sender, buf);
+			}
+			free(buf);
+		    }
+
+		    if (policy == OTRL_POLICY_OPPORTUNISTIC ||
+			    policy == OTRL_POLICY_ALWAYS) {
+			/* Send a key exchange message to try to start up
+			 * the secure conversation */
+			err = otrl_proto_create_key_exchange(us, &msgtosend,
+				context, 0, ops->create_privkey, opdata);
+			if (!send_or_error(ops, opdata, err, accountname,
+				    protocol, sender, msgtosend)) {
+			    context->state = CONN_SETUP;
+			    if (ops->update_context_list) {
+				ops->update_context_list(opdata);
+			    }
+			}
+			free(msgtosend);
+		    }
+
+		    break;
+		case CONN_CONNECTED:
+		    err = otrl_proto_accept_data(&plaintext, &tlvs, context,
+			    message);
+		    if (err) {
+			format = "We received a malformed "
+				"data message from %s.";
+			buf = malloc(strlen(format) + strlen(sender) - 1);
+			if (buf) {
+			    sprintf(buf, format, sender);
+			    if (ops->notify) {
+				ops->notify(opdata, OTRL_NOTIFY_ERROR,
+					accountname, protocol, sender,
+					"OTR Error", buf, NULL);
+			    }
+			    free(buf);
+			}
+			if (ops->inject_message) {
+			    ops->inject_message(opdata, accountname, protocol,
+				    sender, "?OTR Error: You transmitted "
+					    "a malformed data message");
+			}
+			ignore_message = 1;
+			break;
+		    }
+
+		    /* If the other side told us he's disconnected his
+		     * private connection, make a note of that so we
+		     * don't try sending anything else to him. */
+		    if (otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED)) {
+			context->their_keyid = 0;
+		    }
+		    
+		    if (plaintext[0] == '\0') {
+			/* If it's a heartbeat (an empty message), don't
+			 * display it to the user, but log a debug message. */
+			format = "Heartbeat received from %s.\n";
+			buf = malloc(strlen(format) + strlen(sender) - 1);
+			if (buf) {
+			    sprintf(buf, format, sender);
+			    if (ops->log_message) {
+				ops->log_message(opdata, buf);
+			    }
+			    free(buf);
+			}
+			ignore_message = 1;
+		    } else if (ignore_message == 0 &&
+			    context->their_keyid > 0) {
+			/* If it's *not* a heartbeat, and we haven't
+			 * sent anything in a while, also send a
+			 * heartbeat. */
+			time_t now = time(NULL);
+			if (context->lastsent < (now - HEARTBEAT_INTERVAL)) {
+			    char *heartbeat;
+
+			    /* Create the heartbeat message */
+			    err = otrl_proto_create_data(&heartbeat,
+				    context, "", NULL);
+			    if (!err) {
+				/* Send it, and log a debug message */
+				if (ops->inject_message) {
+				    ops->inject_message(opdata, accountname,
+					    protocol, sender, heartbeat);
+				}
+				free(heartbeat);
+
+				context->lastsent = now;
+
+				/* Log a debug message */
+				format = "Heartbeat sent to %s.\n";
+				buf = malloc(strlen(format) + strlen(sender)
+					- 1);
+				if (buf) {
+				    sprintf(buf, format, sender);
+				    if (ops->log_message) {
+					ops->log_message(opdata, buf);
+				    }
+				    free(buf);
+				}
+			    }
+			}
+		    }
+
+		    /* Return the TLVs even if ignore_message == 1 so
+		     * that we can attach TLVs to heartbeats. */
+		    if (tlvsp) {
+			*tlvsp = tlvs;
+		    } else {
+			otrl_tlv_free(tlvs);
+		    }
+
+		    if (ignore_message != 1) {
+			*newmessagep = plaintext;
+			ignore_message = 0;
+		    } else {
+			free(plaintext);
+		    }
+		    break;
+	    }
+	    break;
+	case OTR_ERROR:
+	    switch(state) {
+		gcry_error_t err;
+		char *msgtosend;
+		case CONN_UNCONNECTED:
+		case CONN_SETUP:
+		    if (policy == OTRL_POLICY_OPPORTUNISTIC ||
+			    policy == OTRL_POLICY_ALWAYS) {
+			/* The other end clearly supports OTR, so try to
+			 * start up a private conversation */
+			err = otrl_proto_create_key_exchange(us, &msgtosend,
+				context, 0, ops->create_privkey, opdata);
+			if (!send_or_error(ops, opdata, err, accountname,
+				    protocol, sender, msgtosend)) {
+			    context->state = CONN_SETUP;
+			    if (ops->update_context_list) {
+				ops->update_context_list(opdata);
+			    }
+			}
+			free(msgtosend);
+		    }
+		    break;
+		case CONN_CONNECTED:
+		    /* Mark the last message we sent as eligible for
+		     * retransmission */
+		    context->may_retransmit = 1;
+		    break;
+	    }
+	    /* In any event, display the error message, with the
+	     * display_otr_message callback, if possible */
+	    if (ops->display_otr_message) {
+		const char *otrerror = strstr(message, "?OTR Error:");
+		if (otrerror) {
+		    /* Skip the leading '?' */
+		    ++otrerror;
+		} else {
+		    otrerror = message;
+		}
+		if (!ops->display_otr_message(opdata, accountname, protocol,
+			    sender, otrerror)) {
+		    ignore_message = 1;
+		}
+	    }
+	    break;
+	case OTR_TAGGEDPLAINTEXT:
+	    /* Strip the tag from the message */
+	    tag = strstr(message, OTR_MESSAGE_TAG);
+	    if (tag) {
+		size_t taglen = strlen(OTR_MESSAGE_TAG);
+		size_t restlen = strlen(tag + taglen);
+		memmove(tag, tag+taglen, restlen+1);
+	    }
+	    /* FALLTHROUGH */
+	case OTR_NOTOTR:
+	    switch(state) {
+		char *buf;
+		char *format;
+		case CONN_UNCONNECTED:
+		case CONN_SETUP:
+		    if (policy == OTRL_POLICY_OPPORTUNISTIC ||
+			    policy == OTRL_POLICY_ALWAYS) {
+			if (msgtype == OTR_TAGGEDPLAINTEXT) {
+			    /* Send a Key Exchange in response */
+
+			    char *msgtosend;
+			    gcry_error_t err;
+
+			    err = otrl_proto_create_key_exchange(us,
+				    &msgtosend, context, 0,
+				    ops->create_privkey, opdata);
+
+			    if (!send_or_error(ops, opdata, err, accountname,
+					protocol, sender, msgtosend)) {
+				context->state = CONN_SETUP;
+				if (ops->update_context_list) {
+				    ops->update_context_list(opdata);
+				}
+			    }
+			    free(msgtosend);
+			}
+		    }
+
+		    /* If the policy is ALWAYS, we must warn about
+		     * receiving an unencrypted message, so just
+		     * FALLTHROUGH. */
+
+		    if (policy != OTRL_POLICY_ALWAYS) {
+			/* Just display the message. */
+			break;
+		    }
+		case CONN_CONNECTED:
+		    /* Not fine.  Let both users know. */
+
+		    /* Don't use g_strdup_printf here, because someone
+		     * (not us) is going to free() the *message pointer,
+		     * not g_free() it. */
+		    format = "<b>The following message received from %s was "
+			"<i>not</i> encrypted: [</b>%s<b>]</b>";
+		    buf = malloc(strlen(format) + strlen(context->username)
+			    + strlen(message) - 3);
+			    /* Remove "%s%s", add username + message + '\0' */
+		    if (buf) {
+			sprintf(buf, format, context->username, message);
+			if (ops->display_otr_message) {
+			    if (!ops->display_otr_message(opdata, accountname,
+					protocol, sender, buf)) {
+				ignore_message = 1;
+			    }
+			}
+			if (ignore_message != 1) {
+			    *newmessagep = buf;
+			    ignore_message = 0;
+			} else {
+			    free(buf);
+			}
+		    }
+		    format = "?OTR Error: You sent unencrypted data to %s, "
+			    "who was expecting encrypted messages from you.";
+		    buf = malloc(strlen(format) + strlen(context->accountname)
+			    - 1);
+		    if (buf) {
+			sprintf(buf, format, context->accountname);
+			if (ops->inject_message) {
+			    ops->inject_message(opdata, accountname, protocol,
+				    sender, buf);
+			}
+			free(buf);
+		    }
+
+		    break;
+	    }
+	    break;
+	case OTR_UNKNOWN:
+	    /* We received an OTR message we didn't recognize.  Ignore
+	     * it, but make a log entry. */
+	    if (ops->log_message) {
+		const char *format = "Unrecognized OTR message received "
+		    "from %s.\n";
+		char *buf = malloc(strlen(format) + strlen(sender) - 1);
+		if (buf) {
+		    sprintf(buf, format, sender);
+		    ops->log_message(opdata, buf);
+		    free(buf);
+		}
+	    }
+	    if (ignore_message == -1) ignore_message = 1;
+	    break;
+    }
+
+    if (ignore_message == -1) ignore_message = 0;
+    return ignore_message;
+}
+
+/* Put a connection into the UNCONNECTED state, first sending the
+ * other side a notice that we're doing so if we're currently CONNECTED,
+ * and we think he's logged in. */
+void otrl_message_disconnect(OtrlUserState us, OtrlMessageAppOps *ops,
+	void *opdata, const char *accountname, const char *protocol,
+	const char *username)
+{
+    ConnContext *context = otrl_context_find(us, username, accountname,
+	    protocol, 0, NULL, NULL, NULL);
+
+    if (!context) return;
+
+    if (context->state == CONN_CONNECTED && context->their_keyid > 0 &&
+	    ops->is_logged_in &&
+	    ops->is_logged_in(opdata, accountname, protocol, username) == 1) {
+	if (ops->inject_message) {
+	    char *encmsg = NULL;
+	    gcry_error_t err;
+	    OtrlTLV *tlv = otrl_tlv_new(OTRL_TLV_DISCONNECTED, 0, NULL);
+
+	    err = otrl_proto_create_data(&encmsg, context, "", tlv);
+	    if (!err) {
+		ops->inject_message(opdata, accountname, protocol,
+			username, encmsg);
+	    }
+	    free(encmsg);
+	}
+    }
+
+    otrl_context_force_disconnect(context);
+    if (ops->update_context_list) {
+	ops->update_context_list(opdata);
+    }
+}
diff --git a/src/message.h b/src/message.h
new file mode 100644
index 0000000..ad20361
--- /dev/null
+++ b/src/message.h
@@ -0,0 +1,191 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <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 __MESSAGE_H__
+#define __MESSAGE_H__
+
+typedef enum {
+    OTRL_NOTIFY_ERROR,
+    OTRL_NOTIFY_WARNING,
+    OTRL_NOTIFY_INFO
+} OtrlNotifyLevel;
+
+typedef enum {
+    OTRL_POLICY_OPPORTUNISTIC,
+    OTRL_POLICY_NEVER,
+    OTRL_POLICY_MANUAL,
+    OTRL_POLICY_ALWAYS
+} OtrlPolicy;
+
+#define OTRL_POLICY_DEFAULT OTRL_POLICY_OPPORTUNISTIC
+
+typedef struct s_OTRConfirmResponse OTRConfirmResponse;
+
+typedef struct s_OtrlMessageAppOps {
+    /* Return the OTR policy for the given context. */
+    OtrlPolicy (*policy)(void *opdata, ConnContext *context);
+
+    /* Create a private key for the given accountname/protocol if
+     * desired. */
+    void (*create_privkey)(void *opdata, const char *accountname,
+	    const char *protocol);
+
+    /* Report whether you think the given user is online.  Return 1 if
+     * you think he is, 0 if you think he isn't, -1 if you're not sure.
+     *
+     * If you return 1, messages such as heartbeats or other
+     * notifications may be sent to the user, which could result in "not
+     * logged in" errors if you're wrong. */
+    int (*is_logged_in)(void *opdata, const char *accountname,
+	    const char *protocol, const char *recipient);
+
+    /* Send the given IM to the given recipient from the given
+     * accountname/protocol. */
+    void (*inject_message)(void *opdata, const char *accountname,
+	    const char *protocol, const char *recipient, const char *message);
+
+    /* Display a notification message for a particular accountname /
+     * protocol / username conversation. */
+    void (*notify)(void *opdata, OtrlNotifyLevel level,
+	    const char *accountname, const char *protocol,
+	    const char *username, const char *title,
+	    const char *primary, const char *secondary);
+
+    /* Display an OTR control message for a particular accountname /
+     * protocol / username conversation.  Return 0 if you are able to
+     * successfully display it.  If you return non-0 (or if this
+     * function is NULL), the control message will be displayed inline,
+     * as a received message. */
+    int (*display_otr_message)(void *opdata, const char *accountname,
+	    const char *protocol, const char *username, const char *msg);
+
+    /* When the list of ConnContexts changes (including a change in
+     * 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
+     * for the given protocol id */
+    const char *(*protocol_name)(void *opdata, const char *protocol);
+
+    /* Deallocate a string allocated by protocol_name */
+    void (*protocol_name_free)(void *opdata, const char *protocol_name);
+
+    /* Ask the user of the given accountname to confirm an unknown
+     * fingerprint (contained in kem) for the given username of the
+     * given protocol.  When the user has decided, call
+     * response_cb(us, ops, opdata, response_data, resp) where resp is 1
+     * to accept the fingerprint, 0 to reject it, and -1 if the user
+     * didn't make a choice (say, by destroying the dialog window).
+     * BE SURE to call response_cb no matter what happens. */
+    void (*confirm_fingerprint)(OtrlUserState us, void *opdata,
+	    const char *accountname, const char *protocol,
+	    const char *username, OTRKeyExchangeMsg kem,
+	    void (*response_cb)(OtrlUserState us,
+		struct s_OtrlMessageAppOps *ops, void *opdata,
+		OTRConfirmResponse *response_data, int resp),
+	    OTRConfirmResponse *response_data);
+
+    /* The list of known fingerprints has changed.  Write them to disk. */
+    void (*write_fingerprints)(void *opdata);
+
+    /* A ConnContext has entered a secure state. */
+    void (*gone_secure)(void *opdata, ConnContext *context);
+
+    /* A ConnContext has left a secure state. */
+    void (*gone_insecure)(void *opdata, ConnContext *context);
+
+    /* A ConnContext has received a Key Exchange Message, which is the
+     * same as the one we already knew.  is_reply indicates whether the
+     * Key Exchange Message is a reply to one that we sent to them. */
+    void (*still_secure)(void *opdata, ConnContext *context, int is_reply);
+
+    /* Log a message.  The passed message will end in "\n". */
+    void (*log_message)(void *opdata, const char *message);
+
+} OtrlMessageAppOps;
+
+/* Deallocate a message allocated by other otrl_message_* routines. */
+void otrl_message_free(char *message);
+
+/* Handle a message about to be sent to the network.  It is safe to pass
+ * all messages about to be sent to this routine.  add_appdata is a
+ * function that will be called in the event that a new ConnContext is
+ * created.  It will be passed the data that you supplied, as well as a
+ * pointer to the new ConnContext.  You can use this to add
+ * application-specific information to the ConnContext using the
+ * "context->app" field, for example.  If you don't need to do this, you
+ * can pass NULL for the last two arguments of otrl_message_sending.  
+ *
+ * tlvs is a chain of OtrlTLVs to append to the private message.  It is
+ * usually correct to just pass NULL here.
+ *
+ * If this routine returns non-zero, then the library tried to encrypt
+ * the message, but for some reason failed.  DO NOT send the message in
+ * the clear in that case.
+ * 
+ * If *messagep gets set by the call to something non-NULL, then you
+ * should replace your message with the contents of *messagep, and
+ * send that instead.  Call otrl_message_free(*messagep) when you're
+ * done with it. */
+gcry_error_t otrl_message_sending(OtrlUserState us, OtrlMessageAppOps
+	*ops, void *opdata, const char *accountname, const char *protocol,
+	const char *recipient, const char *message, OtrlTLV *tlvs,
+	char **messagep,
+	void (*add_appdata)(void *data, ConnContext *context),
+	void *data);
+
+/* 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.
+ * It will be passed the data that you supplied, as well as
+ * a pointer to the new ConnContext.  You can use this to add
+ * application-specific information to the ConnContext using the
+ * "context->app" field, for example.  If you don't need to do this, you
+ * can pass NULL for the last two arguments of otrl_message_receiving.  
+ *
+ * If otrl_message_receiving returns 1, then the message you received
+ * was an internal protocol message, and no message should be delivered
+ * to the user.
+ *
+ * If it returns 0, then check if *messagep was set to non-NULL.  If
+ * so, replace the received message with the contents of *messagep, and
+ * deliver that to the user instead.  You must call
+ * otrl_message_free(*messagep) when you're done with it.  If tlvsp is
+ * non-NULL, *tlvsp will be set to a chain of any TLVs that were
+ * transmitted along with this message.  You must call
+ * otrl_tlv_free(*tlvsp) when you're done with those.
+ *
+ * If otrl_message_receiving returns 0 and *messagep is NULL, then this
+ * was an ordinary, non-OTR message, which should just be delivered to
+ * the user without modification. */
+int otrl_message_receiving(OtrlUserState us, OtrlMessageAppOps *ops,
+	void *opdata, const char *accountname, const char *protocol,
+	const char *sender, const char *message, char **newmessagep,
+	OtrlTLV **tlvsp,
+	void (*add_appdata)(void *data, ConnContext *context),
+	void *data);
+
+/* Put a connection into the DISCONNECTED state, first sending the
+ * other side a notice that we're doing so if we're currently CONNECTED,
+ * and we think he's logged in. */
+void otrl_message_disconnect(OtrlUserState us, OtrlMessageAppOps *ops,
+	void *opdata, const char *accountname, const char *protocol,
+	const char *username);
+
+#endif
diff --git a/src/privkey.c b/src/privkey.c
new file mode 100644
index 0000000..828c90c
--- /dev/null
+++ b/src/privkey.c
@@ -0,0 +1,459 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <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 <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+/* libgcrypt headers */
+#include <gcrypt.h>
+
+/* libotr headers */
+#include "proto.h"
+#include "privkey.h"
+
+/* Convert a 20-byte hash value to a 45-byte human-readable value */
+void otrl_privkey_hash_to_human(char human[45], unsigned char hash[20])
+{
+    int word, byte;
+    char *p = human;
+
+    for(word=0; word<5; ++word) {
+	for(byte=0; byte<4; ++byte) {
+	    sprintf(p, "%02X", hash[word*4+byte]);
+	    p += 2;
+	}
+	*(p++) = ' ';
+    }
+    /* Change that last ' ' to a '\0' */
+    --p;
+    *p = '\0';
+}
+
+/* Calculate a human-readable 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. */
+char *otrl_privkey_fingerprint(OtrlUserState us, char fingerprint[45],
+	const char *accountname, const char *protocol)
+{
+    unsigned char hash[20];
+    PrivKey *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);
+
+	/* Now convert it to a human-readable format */
+	otrl_privkey_hash_to_human(fingerprint, hash);
+    } else {
+	return NULL;
+    }
+
+    return fingerprint;
+}
+
+/* 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)
+{
+    FILE *privf;
+    int privfd;
+    struct stat st;
+    char *buf;
+    const char *token;
+    size_t tokenlen;
+    gcry_error_t err;
+    gcry_sexp_t allkeys;
+    size_t i;
+
+    /* Release any old ideas we had about our keys */
+    otrl_privkey_forget_all(us);
+
+    /* Open the privkey file.  We use rb mode so that on WIN32, fread()
+     * reads the same number of bytes that fstat() indicates are in the
+     * file. */
+    privf = fopen(filename, "rb");
+    if (!privf) {
+	err = gcry_error_from_errno(errno);
+	return err;
+    }
+
+    /* Load the data into a buffer */
+    privfd = fileno(privf);
+    if (fstat(privfd, &st)) {
+	err = gcry_error_from_errno(errno);
+	fclose(privf);
+	return err;
+    }
+    buf = malloc(st.st_size);
+    if (!buf && st.st_size > 0) {
+	fclose(privf);
+	return gcry_error(GPG_ERR_ENOMEM);
+    }
+    if (fread(buf, st.st_size, 1, privf) != 1) {
+	err = gcry_error_from_errno(errno);
+	fclose(privf);
+	free(buf);
+	return err;
+    }
+    fclose(privf);
+
+    err = gcry_sexp_new(&allkeys, buf, st.st_size, 0);
+    free(buf);
+    if (err) {
+	return err;
+    }
+
+    token = gcry_sexp_nth_data(allkeys, 0, &tokenlen);
+    if (tokenlen != 8 || strncmp(token, "privkeys", 8)) {
+	gcry_sexp_release(allkeys);
+	return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
+    }
+
+    /* Get each account */
+    for(i=1; i<gcry_sexp_length(allkeys); ++i) {
+	gcry_sexp_t names, protos, privs;
+	char *name, *proto;
+	gcry_sexp_t accounts;
+	PrivKey *p;
+	
+	/* Get the ith "account" S-exp */
+	accounts = gcry_sexp_nth(allkeys, i);
+	
+	/* It's really an "account" S-exp? */
+	token = gcry_sexp_nth_data(accounts, 0, &tokenlen);
+	if (tokenlen != 7 || strncmp(token, "account", 7)) {
+	    gcry_sexp_release(accounts);
+	    return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
+	}
+	/* Extract the name, protocol, and privkey S-exps */
+	names = gcry_sexp_find_token(accounts, "name", 0);
+	protos = gcry_sexp_find_token(accounts, "protocol", 0);
+	privs = gcry_sexp_find_token(accounts, "private-key", 0);
+	gcry_sexp_release(accounts);
+	if (!names || !protos || !privs) {
+	    gcry_sexp_release(names);
+	    gcry_sexp_release(protos);
+	    gcry_sexp_release(privs);
+	    return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
+	}
+	/* Extract the actual name and protocol */
+	token = gcry_sexp_nth_data(names, 1, &tokenlen);
+	if (!token) {
+	    gcry_sexp_release(names);
+	    gcry_sexp_release(protos);
+	    gcry_sexp_release(privs);
+	    return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
+	}
+	name = malloc(tokenlen + 1);
+	if (!name) {
+	    gcry_sexp_release(names);
+	    gcry_sexp_release(protos);
+	    gcry_sexp_release(privs);
+	    return gcry_error(GPG_ERR_ENOMEM);
+	}
+	memmove(name, token, tokenlen);
+	name[tokenlen] = '\0';
+	gcry_sexp_release(names);
+
+	token = gcry_sexp_nth_data(protos, 1, &tokenlen);
+	if (!token) {
+	    free(name);
+	    gcry_sexp_release(protos);
+	    gcry_sexp_release(privs);
+	    return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
+	}
+	proto = malloc(tokenlen + 1);
+	if (!proto) {
+	    free(name);
+	    gcry_sexp_release(protos);
+	    gcry_sexp_release(privs);
+	    return gcry_error(GPG_ERR_ENOMEM);
+	}
+	memmove(proto, token, tokenlen);
+	proto[tokenlen] = '\0';
+	gcry_sexp_release(protos);
+
+	/* Make a new PrivKey entry */
+	p = malloc(sizeof(*p));
+	if (!p) {
+	    free(name);
+	    free(proto);
+	    gcry_sexp_release(privs);
+	    return gcry_error(GPG_ERR_ENOMEM);
+	}
+
+	/* Fill it in and link it up */
+	p->accountname = name;
+	p->protocol = proto;
+	p->privkey = privs;
+	p->next = us->privkey_root;
+	if (p->next) {
+	    p->next->tous = &(p->next);
+	}
+	p->tous = &(us->privkey_root);
+	us->privkey_root = p;
+	err = otrl_proto_make_pubkey(&(p->pubkey_data), &(p->pubkey_datalen),
+	    p->privkey);
+	if (err) {
+	    otrl_privkey_forget(p);
+	    return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
+	}
+    }
+
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+static gcry_error_t sexp_write(FILE *privf, gcry_sexp_t sexp)
+{
+    size_t buflen;
+    char *buf;
+
+    buflen = gcry_sexp_sprint(sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
+    buf = malloc(buflen);
+    if (buf == NULL && buflen > 0) {
+	return gcry_error(GPG_ERR_ENOMEM);
+    }
+    gcry_sexp_sprint(sexp, GCRYSEXP_FMT_ADVANCED, buf, buflen);
+    
+    fprintf(privf, "%s", buf);
+    free(buf);
+
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+static gcry_error_t account_write(FILE *privf, const char *accountname,
+	const char *protocol, gcry_sexp_t privkey)
+{
+    gcry_error_t err;
+    gcry_sexp_t names, protos;
+
+    fprintf(privf, " (account\n");
+
+    err = gcry_sexp_build(&names, NULL, "(name %s)", accountname);
+    if (!err) {
+	err = sexp_write(privf, names);
+	gcry_sexp_release(names);
+    }
+    if (!err) err = gcry_sexp_build(&protos, NULL, "(protocol %s)", protocol);
+    if (!err) {
+	err = sexp_write(privf, protos);
+	gcry_sexp_release(protos);
+    }
+    if (!err) err = sexp_write(privf, privkey);
+
+    fprintf(privf, " )\n");
+
+    return err;
+}
+
+/* Generate a private DSA key for a given account, storing it into a
+ * file on disk, and loading it into the given OtrlUserState.  Overwrite any
+ * previously generated keys for that account in that OtrlUserState. */
+gcry_error_t otrl_privkey_generate(OtrlUserState us, const char *filename,
+	const char *accountname, const char *protocol)
+{
+    gcry_error_t err;
+    gcry_sexp_t key, parms, privkey;
+    FILE *privf;
+#ifndef WIN32
+    mode_t oldmask;
+#endif
+    static const char *parmstr = "(genkey (dsa (nbits 4:1024)))";
+    PrivKey *p;
+
+    /* Create a DSA key */
+    err = gcry_sexp_new(&parms, parmstr, strlen(parmstr), 0);
+    if (err) {
+	return err;
+    }
+    err = gcry_pk_genkey(&key, parms);
+    gcry_sexp_release(parms);
+    if (err) {
+	return err;
+    }
+
+    /* Extract the privkey */
+    privkey = gcry_sexp_find_token(key, "private-key", 0);
+    gcry_sexp_release(key);
+
+    /* Output the other keys we know */
+#ifndef WIN32
+    oldmask = umask(077);
+#endif
+    privf = fopen(filename, "w");
+    if (!privf) {
+	err = gcry_error_from_errno(errno);
+	gcry_sexp_release(privkey);
+	return err;
+    }
+
+    fprintf(privf, "(privkeys\n");
+
+    for (p=us->privkey_root; p; p=p->next) {
+	/* Skip this one if our new key replaces it */
+	if (!strcmp(p->accountname, accountname) &&
+		!strcmp(p->protocol, protocol)) {
+	    continue;
+	}
+
+	account_write(privf, p->accountname, p->protocol, p->privkey);
+    }
+    account_write(privf, accountname, protocol, privkey);
+    gcry_sexp_release(privkey);
+    fprintf(privf, ")\n");
+    fclose(privf);
+#ifndef WIN32
+    umask(oldmask);
+#endif
+
+    return otrl_privkey_read(us, filename);
+}
+
+/* Convert a hex character to a value */
+static unsigned int ctoh(char c)
+{
+    if (c >= '0' && c <= '9') return c-'0';
+    if (c >= 'a' && c <= 'f') return c-'a'+10;
+    if (c >= 'A' && c <= 'F') return c-'A'+10;
+    return 0;  /* Unknown hex char */
+}
+
+/* Read the fingerprint store from a file on disk into the given
+ * OtrlUserState.  Use add_app_data to add application data to each
+ * ConnContext so created. */
+gcry_error_t otrl_privkey_read_fingerprints(OtrlUserState us,
+	const char *filename,
+	void (*add_app_data)(void *data, ConnContext *context),
+	void  *data)
+{
+    FILE *storef;
+    gcry_error_t err;
+    ConnContext *context;
+    char storeline[1000];
+    unsigned char fingerprint[20];
+    size_t maxsize = sizeof(storeline);
+
+    storef = fopen(filename, "r");
+    if (!storef) {
+	err = gcry_error_from_errno(errno);
+	return err;
+    }
+    while(fgets(storeline, maxsize, storef)) {
+	char username[sizeof(storeline)];
+	char accountname[sizeof(storeline)];
+	char protocol[sizeof(storeline)];
+	char hex[sizeof(storeline)];
+	int res, i, j;
+	/* Parse the line, which should be of the form:
+	 *    username\taccountname\tprotocol\t40_hex_nybbles\n          */
+	res = sscanf(storeline, "%s %s %s %s", username, accountname,
+		protocol, hex);
+	if (res != 4) continue;
+	if (strlen(hex) != 40) continue;
+	for(j=0, i=0; i<40; i+=2) {
+	    fingerprint[j++] = (ctoh(hex[i]) << 4) + (ctoh(hex[i+1]));
+	}
+	/* Get the context for this user, adding if not yet present */
+	context = otrl_context_find(us, username, accountname, protocol,
+		1, NULL, add_app_data, data);
+	/* Add the fingerprint if not already there */
+	otrl_context_find_fingerprint(context, fingerprint, 1, NULL);
+    }
+    fclose(storef);
+
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Write the fingerprint store from a given OtrlUserState to a file on disk. */
+gcry_error_t otrl_privkey_write_fingerprints(OtrlUserState us,
+	const char *filename)
+{
+    FILE *storef;
+    gcry_error_t err;
+    ConnContext *context;
+    Fingerprint *fprint;
+
+    storef = fopen(filename, "w");
+    if (!storef) {
+	err = gcry_error_from_errno(errno);
+	return err;
+    }
+    for(context = us->context_root; context; context = context->next) {
+	/* Don't both with the first (fingerprintless) entry. */
+	for (fprint = context->fingerprint_root.next; fprint;
+		fprint = fprint->next) {
+	    int i;
+	    fprintf(storef, "%s\t%s\t%s\t", context->username,
+		    context->accountname, context->protocol);
+	    for(i=0;i<20;++i)
+		fprintf(storef, "%02x", fprint->fingerprint[i]);
+	    fprintf(storef, "\n");
+	}
+    }
+    fclose(storef);
+
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Fetch the private key from the given OtrlUserState associated with
+ * the given account */
+PrivKey *otrl_privkey_find(OtrlUserState us, const char *accountname,
+	const char *protocol)
+{
+    PrivKey *p;
+    if (!accountname || !protocol) return NULL;
+
+    for(p=us->privkey_root; p; p=p->next) {
+	if (!strcmp(p->accountname, accountname) &&
+		!strcmp(p->protocol, protocol)) {
+	    return p;
+	}
+    }
+    return NULL;
+}
+
+/* Forget a private key */
+void otrl_privkey_forget(PrivKey *privkey)
+{
+    free(privkey->accountname);
+    free(privkey->protocol);
+    gcry_sexp_release(privkey->privkey);
+    free(privkey->pubkey_data);
+
+    /* Re-link the list */
+    *(privkey->tous) = privkey->next;
+    if (privkey->next) {
+	privkey->next->tous = privkey->tous;
+    }
+
+    /* Free the privkey struct */
+    free(privkey);
+}
+
+/* Forget all private keys in a given OtrlUserState. */
+void otrl_privkey_forget_all(OtrlUserState us)
+{
+    while (us->privkey_root) {
+	otrl_privkey_forget(us->privkey_root);
+    }
+}
diff --git a/src/privkey.h b/src/privkey.h
new file mode 100644
index 0000000..1fe0608
--- /dev/null
+++ b/src/privkey.h
@@ -0,0 +1,80 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <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 __PRIVKEY_H__
+#define __PRIVKEY_H__
+
+#include <gcrypt.h>
+
+typedef struct s_PrivKey {
+    char *accountname;
+    char *protocol;
+    gcry_sexp_t privkey;
+    unsigned char *pubkey_data;
+    size_t pubkey_datalen;
+    struct s_PrivKey *next;
+    struct s_PrivKey **tous;
+} PrivKey;
+
+#include "context.h"
+#include "userstate.h"
+
+/* Convert a 20-byte hash value to a 45-byte human-readable value */
+void otrl_privkey_hash_to_human(char human[45], unsigned char hash[20]);
+
+/* Calculate a human-readable 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. */
+char *otrl_privkey_fingerprint(OtrlUserState us, char fingerprint[45],
+	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);
+
+/* Generate a private DSA key for a given account, storing it into a
+ * file on disk, and loading it into the given OtrlUserState.  Overwrite any
+ * previously generated keys for that account in that OtrlUserState. */
+gcry_error_t otrl_privkey_generate(OtrlUserState us, const char *filename,
+	const char *accountname, const char *protocol);
+
+/* Read the fingerprint store from a file on disk into the given
+ * OtrlUserState.  Use add_app_data to add application data to each
+ * ConnContext so created. */
+gcry_error_t otrl_privkey_read_fingerprints(OtrlUserState us,
+	const char *filename,
+	void (*add_app_data)(void *data, ConnContext *context),
+	void  *data);
+
+/* Write the fingerprint store from a given OtrlUserState to a file on disk. */
+gcry_error_t otrl_privkey_write_fingerprints(OtrlUserState us,
+	const char *filename);
+
+/* Fetch the private key from the given OtrlUserState associated with
+ * the given account */
+PrivKey *otrl_privkey_find(OtrlUserState us, const char *accountname,
+	const char *protocol);
+
+/* Forget a private key */
+void otrl_privkey_forget(PrivKey *privkey);
+
+/* Forget all private keys in a given OtrlUserState. */
+void otrl_privkey_forget_all(OtrlUserState us);
+
+#endif
diff --git a/src/proto.c b/src/proto.c
new file mode 100644
index 0000000..349e101
--- /dev/null
+++ b/src/proto.c
@@ -0,0 +1,1029 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <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
+ */
+
+/* OTR Protocol implementation.  This file should be independent of
+ * gaim, so that it can be used to make other clients. */
+
+/* system headers */
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+/* libgcrypt headers */
+#include <gcrypt.h>
+
+/* libotr headers */
+#include "b64.h"
+#include "privkey.h"
+#include "proto.h"
+#include "mem.h"
+#include "version.h"
+#include "tlv.h"
+
+#undef DEBUG
+
+#ifdef DEBUG
+static void debug_data(const char *title, const unsigned char *data,
+	size_t len)
+{
+    size_t i;
+    fprintf(stderr, "%s: ", title);
+    for(i=0;i<len;++i) {
+	fprintf(stderr, "%02x", data[i]);
+    }
+    fprintf(stderr, "\n");
+}
+
+static void debug_int(const char *title, const unsigned char *data)
+{
+    unsigned int v =
+	(data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
+    fprintf(stderr, "%s: %u (0x%x)\n", title, v, v);
+}
+#else
+#define debug_data(t,b,l)
+#define debug_int(t,b)
+#endif
+
+#define write_int(x) do { \
+	bufp[0] = ((x) >> 24) & 0xff; \
+	bufp[1] = ((x) >> 16) & 0xff; \
+	bufp[2] = ((x) >> 8) & 0xff; \
+	bufp[3] = (x) & 0xff; \
+	bufp += 4; lenp -= 4; \
+    } while(0)
+
+#define write_mpi(x,nx,dx) do { \
+	write_int(nx); \
+	gcry_mpi_print(format, bufp, lenp, NULL, (x)); \
+	debug_data((dx), bufp, (nx)); \
+	bufp += (nx); lenp -= (nx); \
+    } while(0)
+
+#define require_len(l) do { \
+	if (lenp < (l)) goto invval; \
+    } while(0)
+
+#define read_int(x) do { \
+	require_len(4); \
+	(x) = (bufp[0] << 24) | (bufp[1] << 16) | (bufp[2] << 8) | bufp[3]; \
+	bufp += 4; lenp -= 4; \
+    } while(0)
+
+#define read_mpi(x) do { \
+	size_t mpilen; \
+	read_int(mpilen); \
+	require_len(mpilen); \
+	gcry_mpi_scan(&(x), GCRYMPI_FMT_USG, bufp, mpilen, NULL); \
+	bufp += mpilen; lenp -= mpilen; \
+    } while(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)
+{
+    /* 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) {
+	fprintf(stderr, "Expected libotr API version %u.%u.%u incompatible "
+		"with actual version %u.%u.%u.  Aborting.\n",
+		ver_major, ver_minor, ver_sub,
+		OTRL_VERSION_MAJOR, OTRL_VERSION_MINOR, OTRL_VERSION_SUB);
+	exit(1);
+    }
+
+    /* Initialize the memory module */
+    otrl_mem_init();
+
+    /* Initialize the DH module */
+    otrl_dh_init();
+}
+
+/* Return a pointer to a static string containing the version number of
+ * the OTR library. */
+const char *otrl_version(void)
+{
+    return OTRL_VERSION;
+}
+
+/* Create a public key block from a private key */
+gcry_error_t otrl_proto_make_pubkey(unsigned char **pubbufp, size_t *publenp,
+	gcry_sexp_t privkey)
+{
+    gcry_mpi_t p,q,g,y;
+    gcry_sexp_t dsas,ps,qs,gs,ys;
+    size_t np,nq,ng,ny;
+    enum gcry_mpi_format format = GCRYMPI_FMT_USG;
+    unsigned char *bufp;
+    size_t lenp;
+
+    *pubbufp = NULL;
+    *publenp = 0;
+
+    /* Extract the public parameters */
+    dsas = gcry_sexp_find_token(privkey, "dsa", 0);
+    if (dsas == NULL) {
+	return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
+    }
+    ps = gcry_sexp_find_token(dsas, "p", 0);
+    qs = gcry_sexp_find_token(dsas, "q", 0);
+    gs = gcry_sexp_find_token(dsas, "g", 0);
+    ys = gcry_sexp_find_token(dsas, "y", 0);
+    gcry_sexp_release(dsas);
+    if (!ps || !qs || !gs || !ys) {
+	gcry_sexp_release(ps);
+	gcry_sexp_release(qs);
+	gcry_sexp_release(gs);
+	gcry_sexp_release(ys);
+	return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
+    }
+    p = gcry_sexp_nth_mpi(ps, 1, GCRYMPI_FMT_USG);
+    gcry_sexp_release(ps);
+    q = gcry_sexp_nth_mpi(qs, 1, GCRYMPI_FMT_USG);
+    gcry_sexp_release(qs);
+    g = gcry_sexp_nth_mpi(gs, 1, GCRYMPI_FMT_USG);
+    gcry_sexp_release(gs);
+    y = gcry_sexp_nth_mpi(ys, 1, GCRYMPI_FMT_USG);
+    gcry_sexp_release(ys);
+    if (!p || !q || !g || !y) {
+	gcry_mpi_release(p);
+	gcry_mpi_release(q);
+	gcry_mpi_release(g);
+	gcry_mpi_release(y);
+	return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
+    }
+
+    *publenp = 0;
+    gcry_mpi_print(format, NULL, 0, &np, p);
+    *publenp += np + 4;
+    gcry_mpi_print(format, NULL, 0, &nq, q);
+    *publenp += nq + 4;
+    gcry_mpi_print(format, NULL, 0, &ng, g);
+    *publenp += ng + 4;
+    gcry_mpi_print(format, NULL, 0, &ny, y);
+    *publenp += ny + 4;
+
+    *pubbufp = malloc(*publenp);
+    if (*pubbufp == NULL) {
+	gcry_mpi_release(p);
+	gcry_mpi_release(q);
+	gcry_mpi_release(g);
+	gcry_mpi_release(y);
+	return gcry_error(GPG_ERR_ENOMEM);
+    }
+    bufp = *pubbufp;
+    lenp = *publenp;
+
+    write_mpi(p,np,"P");
+    write_mpi(q,nq,"Q");
+    write_mpi(g,ng,"G");
+    write_mpi(y,ny,"Y");
+
+    gcry_mpi_release(p);
+    gcry_mpi_release(q);
+    gcry_mpi_release(g);
+    gcry_mpi_release(y);
+
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Store some MAC keys to be revealed later */
+static gcry_error_t reveal_macs(ConnContext *context,
+	DH_sesskeys *sess1, DH_sesskeys *sess2)
+{
+    unsigned int numnew = sess1->rcvmacused + sess1->sendmacused +
+	sess2->rcvmacused + sess2->sendmacused;
+    unsigned int newnumsaved;
+    unsigned char *newmacs;
+    
+    /* Is there anything to do? */
+    if (numnew == 0) return gcry_error(GPG_ERR_NO_ERROR);
+
+    newnumsaved = context->numsavedkeys + numnew;
+    newmacs = realloc(context->saved_mac_keys,
+	    newnumsaved * 20);
+    if (!newmacs) {
+	return gcry_error(GPG_ERR_ENOMEM);
+    }
+    if (sess1->rcvmacused) {
+	memmove(newmacs + context->numsavedkeys * 20, sess1->rcvmackey, 20);
+	context->numsavedkeys++;
+    }
+    if (sess1->sendmacused) {
+	memmove(newmacs + context->numsavedkeys * 20, sess1->sendmackey, 20);
+	context->numsavedkeys++;
+    }
+    if (sess2->rcvmacused) {
+	memmove(newmacs + context->numsavedkeys * 20, sess2->rcvmackey, 20);
+	context->numsavedkeys++;
+    }
+    if (sess2->sendmacused) {
+	memmove(newmacs + context->numsavedkeys * 20, sess2->sendmackey, 20);
+	context->numsavedkeys++;
+    }
+    context->saved_mac_keys = newmacs;
+
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Make a new DH key for us, and rotate old old ones.  Be sure to keep
+ * the sesskeys array in sync. */
+static gcry_error_t rotate_dh_keys(ConnContext *context)
+{
+    gcry_error_t err;
+
+    /* Rotate the keypair */
+    otrl_dh_keypair_free(&(context->our_old_dh_key));
+    memmove(&(context->our_old_dh_key), &(context->our_dh_key),
+	    sizeof(DH_keypair));
+
+    /* Rotate the session keys */
+    err = reveal_macs(context, &(context->sesskeys[1][0]),
+	    &(context->sesskeys[1][1]));
+    if (err) return err;
+    otrl_dh_session_free(&(context->sesskeys[1][0]));
+    otrl_dh_session_free(&(context->sesskeys[1][1]));
+    memmove(&(context->sesskeys[1][0]), &(context->sesskeys[0][0]),
+	    sizeof(DH_sesskeys));
+    memmove(&(context->sesskeys[1][1]), &(context->sesskeys[0][1]),
+	    sizeof(DH_sesskeys));
+
+    /* Create a new DH key */
+    otrl_dh_gen_keypair(DH1536_GROUP_ID, &(context->our_dh_key));
+    context->our_keyid++;
+
+    /* Make the session keys */
+    if (context->their_y) {
+	err = otrl_dh_session(&(context->sesskeys[0][0]),
+		&(context->our_dh_key), context->their_y);
+	if (err) return err;
+    }
+    if (context->their_old_y) {
+	err = otrl_dh_session(&(context->sesskeys[0][1]),
+		&(context->our_dh_key), context->their_old_y);
+	if (err) return err;
+    }
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Rotate in a new DH public key for our correspondent.  Be sure to keep
+ * the sesskeys array in sync. */
+static gcry_error_t rotate_y_keys(ConnContext *context, gcry_mpi_t new_y)
+{
+    gcry_error_t err;
+
+    /* Rotate the public key */
+    gcry_mpi_release(context->their_old_y);
+    context->their_old_y = context->their_y;
+
+    /* Rotate the session keys */
+    err = reveal_macs(context, &(context->sesskeys[0][1]),
+	    &(context->sesskeys[1][1]));
+    if (err) return err;
+    otrl_dh_session_free(&(context->sesskeys[0][1]));
+    otrl_dh_session_free(&(context->sesskeys[1][1]));
+    memmove(&(context->sesskeys[0][1]), &(context->sesskeys[0][0]),
+	    sizeof(DH_sesskeys));
+    memmove(&(context->sesskeys[1][1]), &(context->sesskeys[1][0]),
+	    sizeof(DH_sesskeys));
+
+    /* Copy in the new public key */
+    context->their_y = gcry_mpi_copy(new_y);
+    context->their_keyid++;
+
+    /* Make the session keys */
+    err = otrl_dh_session(&(context->sesskeys[0][0]),
+	    &(context->our_dh_key), context->their_y);
+    if (err) return err;
+    err = otrl_dh_session(&(context->sesskeys[1][0]),
+	    &(context->our_old_dh_key), context->their_y);
+    if (err) return err;
+
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Return a pointer to a newly-allocated OTR query message, customized
+ * with our name.  The caller should free() the result when he's done
+ * with it. */
+char *otrl_proto_default_query_msg(const char *ourname)
+{
+    char *msg;
+    /* Don't use g_strdup_printf here, because someone (not us) is going
+     * to free() the *message pointer, not g_free() it.  We can't
+     * require that they g_free() it, because this pointer will probably
+     * get passed to the main IM application for processing (and
+     * free()ing). */
+    const char *format = "?OTR?\n<b>%s</b> has requested an "
+	    "<a href=\"http://www.cypherpunks.ca/otr/\">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.";
+
+    /* Remove "%s", add '\0' */
+    msg = malloc(strlen(format) + strlen(ourname) - 1);
+    if (!msg) return NULL;
+    sprintf(msg, format, ourname);
+    return msg;
+}
+
+/* Return the Message type of the given message. */
+OTRMessageType otrl_proto_message_type(const char *message)
+{
+    char *otrtag;
+
+    otrtag = strstr(message, "?OTR");
+
+    if (!otrtag) {
+	if (strstr(message, OTR_MESSAGE_TAG)) {
+	    return OTR_TAGGEDPLAINTEXT;
+	} else {
+	    return OTR_NOTOTR;
+	}
+    }
+
+    if (!strncmp(otrtag, "?OTR?", 5)) return OTR_QUERY;
+    if (!strncmp(otrtag, "?OTR:AAEK", 9)) return OTR_KEYEXCH;
+    if (!strncmp(otrtag, "?OTR:AAED", 9)) return OTR_DATA;
+    if (!strncmp(otrtag, "?OTR Error:", 11)) return OTR_ERROR;
+
+    return OTR_UNKNOWN;
+}
+
+/* Create a Key Exchange message for our correspondent.  If we need a
+ * private key and don't have one, create_privkey will be called.  Use
+ * the privkeys from the given OtrlUserState. */
+gcry_error_t otrl_proto_create_key_exchange(OtrlUserState us,
+	char **messagep, ConnContext *context, unsigned char is_reply,
+	void (*create_privkey)(void *create_privkey_data,
+	    const char *accountname, const char *protocol),
+	void *create_privkey_data)
+{
+    gcry_mpi_t r, s;
+    gcry_sexp_t dsas, rs, ss;
+    gcry_sexp_t sigs, hashs;
+    size_t nr, ns, buflen, lenp;
+    unsigned char *buf, *bufp;
+    enum gcry_mpi_format format = GCRYMPI_FMT_USG;
+    unsigned char digest[20];
+    gcry_mpi_t digestmpi;
+    char *base64buf;
+    size_t base64len;
+    size_t pubkeylen;
+    PrivKey *privkey =
+	otrl_privkey_find(us, context->accountname, context->protocol);
+
+    *messagep = NULL;
+
+    if (privkey == NULL) {
+	/* We've got no private key! */
+	if (create_privkey) {
+	    create_privkey(create_privkey_data, context->accountname,
+		    context->protocol);
+	    privkey =
+		otrl_privkey_find(us, context->accountname, context->protocol);
+	}
+    }
+    if (privkey == NULL) {
+	/* We've still got no private key! */
+	return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
+    }
+    
+    /* Make sure we have two keys */
+    while (context->our_keyid < 2) {
+	rotate_dh_keys(context);
+    }
+
+    buflen = 3 + 1 + privkey->pubkey_datalen + 4 + 40;
+	/* header, is_reply, pubkey, keyid, sig */
+    gcry_mpi_print(format, NULL, 0, &pubkeylen, context->our_old_dh_key.pub);
+    buflen += pubkeylen + 4;
+    buf = malloc(buflen);
+    if (buf == NULL) {
+	return gcry_error(GPG_ERR_ENOMEM);
+    }
+    bufp = buf;
+    lenp = buflen;
+    memmove(bufp, "\x00\x01\x0a", 3);  /* header */
+    debug_data("Header", bufp, 3);
+    bufp += 3; lenp -= 3;
+
+    *bufp = is_reply;                  /* is_reply */
+    debug_data("Reply", bufp, 1);
+    bufp += 1; lenp -= 1;
+
+                                       /* DSA pubkey */
+    memmove(bufp, privkey->pubkey_data, privkey->pubkey_datalen);
+    debug_data("DSA key", bufp, privkey->pubkey_datalen);
+    bufp += privkey->pubkey_datalen; lenp -= privkey->pubkey_datalen;
+
+                                       /* keyid */
+    write_int(context->our_keyid - 1);
+    debug_int("Keyid", bufp - 4);
+
+                                       /* DH pubkey */
+    write_mpi(context->our_old_dh_key.pub, pubkeylen, "Pubkey");
+
+    /* Get a hash of the data to be signed */
+    gcry_md_hash_buffer(GCRY_MD_SHA1, digest, buf, bufp-buf);
+    gcry_mpi_scan(&digestmpi, GCRYMPI_FMT_USG, digest, 20, NULL);
+
+    /* Calculate the sig */
+    gcry_sexp_build(&hashs, NULL, "(%m)", digestmpi);
+    gcry_mpi_release(digestmpi);
+    gcry_pk_sign(&sigs, hashs, privkey->privkey);
+    gcry_sexp_release(hashs);
+    dsas = gcry_sexp_find_token(sigs, "dsa", 0);
+    gcry_sexp_release(sigs);
+    rs = gcry_sexp_find_token(dsas, "r", 0);
+    ss = gcry_sexp_find_token(dsas, "s", 0);
+    gcry_sexp_release(dsas);
+    r = gcry_sexp_nth_mpi(rs, 1, GCRYMPI_FMT_USG);
+    gcry_sexp_release(rs);
+    s = gcry_sexp_nth_mpi(ss, 1, GCRYMPI_FMT_USG);
+    gcry_sexp_release(ss);
+    gcry_mpi_print(format, NULL, 0, &nr, r);
+    gcry_mpi_print(format, NULL, 0, &ns, s);
+    memset(bufp, 0, 40);
+    gcry_mpi_print(format, bufp+(20-nr), lenp, NULL, r);
+    debug_data("R", bufp, 20);
+    bufp += 20; lenp -= 20;
+    gcry_mpi_print(format, bufp+(20-ns), lenp, NULL, s);
+    debug_data("S", bufp, 20);
+    bufp += 20; lenp -= 20;
+
+    assert(lenp == 0);
+
+    gcry_mpi_release(r);
+    gcry_mpi_release(s);
+
+    /* Make the base64-encoding. */
+    base64len = ((buflen + 2) / 3) * 4;
+    base64buf = malloc(5 + base64len + 1 + 1);
+    assert(base64buf != NULL);
+    memmove(base64buf, "?OTR:", 5);
+    otrl_base64_encode(base64buf+5, buf, buflen);
+    base64buf[5 + base64len] = '.';
+    base64buf[5 + base64len + 1] = '\0';
+
+    free(buf);
+
+    *messagep = base64buf;
+
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Deallocate an OTRKeyExchangeMsg returned from proto_parse_key_exchange */
+void otrl_proto_free_key_exchange(OTRKeyExchangeMsg kem)
+{
+    if (!kem) return;
+    gcry_sexp_release(kem->digest_sexp);
+    gcry_sexp_release(kem->dsa_pubkey);
+    gcry_mpi_release(kem->dh_pubkey);
+    gcry_sexp_release(kem->dsa_sig);
+    free(kem);
+}
+
+/* Parse a purported Key Exchange message.  Possible error code portions
+ * of the return value:
+ *   GPG_ERR_NO_ERROR:      Success
+ *   GPG_ERR_ENOMEM:        Out of memory condition
+ *   GPG_ERR_INV_VALUE:     The message was not a well-formed Key Exchange
+ *                          message
+ *   GPG_ERR_BAD_SIGNATURE: The signature on the message didn't verify
+ */
+gcry_error_t otrl_proto_parse_key_exchange(OTRKeyExchangeMsg *kemp,
+	const char *msg)
+{
+    char *otrtag, *endtag;
+    unsigned char *rawmsg, *bufp;
+    size_t msglen, rawlen, lenp;
+    gcry_mpi_t p,q,g,e,r,s;
+    unsigned char hash_of_msg[20];
+    gcry_mpi_t hashmpi;
+    const unsigned char *fingerprintstart, *fingerprintend;
+    const unsigned char *signaturestart, *signatureend;
+    OTRKeyExchangeMsg kem = calloc(1, sizeof(struct s_OTRKeyExchangeMsg));
+
+    if (!kem) {
+	*kemp = NULL;
+	return gcry_error(GPG_ERR_ENOMEM);
+    }
+
+    otrtag = strstr(msg, "?OTR:");
+    if (!otrtag) {
+	*kemp = NULL;
+	otrl_proto_free_key_exchange(kem);
+	return gcry_error(GPG_ERR_INV_VALUE);
+    }
+    endtag = strchr(otrtag, '.');
+    if (endtag) {
+	msglen = endtag-otrtag;
+    } else {
+	msglen = strlen(otrtag);
+    }
+
+    /* Base64-decode the message */
+    rawlen = ((msglen-5) / 4) * 3;   /* maximum possible */
+    rawmsg = malloc(rawlen);
+    if (!rawmsg && rawlen > 0) {
+	*kemp = NULL;
+	otrl_proto_free_key_exchange(kem);
+	return gcry_error(GPG_ERR_ENOMEM);
+    }
+    rawlen = otrl_base64_decode(rawmsg, otrtag+5, msglen-5);  /* actual size */
+
+    bufp = rawmsg;
+    lenp = rawlen;
+
+    signaturestart = bufp;
+
+    require_len(3);
+    if (memcmp(bufp, "\x00\x01\x0a", 3)) {
+	/* Invalid header */
+	goto invval;
+    }
+    bufp += 3; lenp -= 3;
+
+    require_len(1);
+    kem->is_reply = *bufp;
+    if (kem->is_reply != 0 && kem->is_reply != 1) {
+	/* Malformed is_reply field */
+	goto invval;
+    }
+    bufp += 1; lenp -= 1;
+
+    fingerprintstart = bufp;
+    /* Read the DSA public key and calculate its fingerprint. */
+    read_mpi(p);
+    read_mpi(q);
+    read_mpi(g);
+    read_mpi(e);
+    fingerprintend = bufp;
+    gcry_md_hash_buffer(GCRY_MD_SHA1, kem->key_fingerprint,
+	    fingerprintstart, fingerprintend-fingerprintstart);
+
+    /* Create the pubkey S-expression. */
+    gcry_sexp_build(&(kem->dsa_pubkey), NULL,
+	    "(public-key (dsa (p %m)(q %m)(g %m)(y %m)))",
+	    p, q, g, e);
+    gcry_mpi_release(p);
+    gcry_mpi_release(q);
+    gcry_mpi_release(g);
+    gcry_mpi_release(e);
+
+    /* Read the key id */
+    read_int(kem->keyid);
+    if (kem->keyid == 0) {
+	/* Not a legal value */
+	goto invval;
+    }
+
+    /* Read the DH public key */
+    read_mpi(kem->dh_pubkey);
+
+    /* Hash the message so we can check the signature */
+    signatureend = bufp;
+    gcry_md_hash_buffer(GCRY_MD_SHA1, hash_of_msg,
+	    signaturestart, signatureend-signaturestart);
+    /* Turn the hash into an mpi, then into a sexp */
+    gcry_mpi_scan(&hashmpi, GCRYMPI_FMT_USG, hash_of_msg, 20, NULL);
+    gcry_sexp_build(&(kem->digest_sexp), NULL, "(%m)", hashmpi);
+    gcry_mpi_release(hashmpi);
+
+    /* Read the signature */
+    require_len(40);
+    gcry_mpi_scan(&r, GCRYMPI_FMT_USG, bufp, 20, NULL);
+    gcry_mpi_scan(&s, GCRYMPI_FMT_USG, bufp+20, 20, NULL);
+    lenp -= 40;
+    gcry_sexp_build(&(kem->dsa_sig), NULL, "(sig-val (dsa (r %m)(s %m)))",
+	    r, s);
+    gcry_mpi_release(r);
+    gcry_mpi_release(s);
+
+    /* That should be everything */
+    if (lenp != 0) goto invval;
+
+    /* Verify the signature */
+    if (gcry_pk_verify(kem->dsa_sig, kem->digest_sexp, kem->dsa_pubkey)) {
+	/* It failed! */
+	otrl_proto_free_key_exchange(kem);
+	free(rawmsg);
+	*kemp = NULL;
+	return gcry_error(GPG_ERR_BAD_SIGNATURE);
+    }
+
+    free(rawmsg);
+    *kemp = kem;
+    return gcry_error(GPG_ERR_NO_ERROR);
+invval:
+    otrl_proto_free_key_exchange(kem);
+    free(rawmsg);
+    *kemp = NULL;
+    return gcry_error(GPG_ERR_INV_VALUE);
+}
+
+/* Deal with a Key Exchange Message once it's been received and passed
+ * all the validity and UI ("accept this fingerprint?") tests.
+ * context/fprint is the ConnContext and Fingerprint to which it
+ * belongs.  Use the given OtrlUserState to look up any necessary
+ * private keys.  It is the caller's responsibility to
+ * otrl_proto_free_key_exchange(kem) when we're done.  If *messagep gets
+ * set to non-NULL by this function, then it's a message that needs to
+ * get sent to the correspondent.  If we need a private key and don't
+ * have one, create_privkey will be called. */
+gcry_error_t otrl_proto_accept_key_exchange(OtrlUserState us,
+	ConnContext *context, Fingerprint *fprint, OTRKeyExchangeMsg kem,
+	char **messagep,
+	void (*create_privkey)(void *create_privkey_data,
+	    const char *accountname, const char *protocol),
+	void *create_privkey_data)
+{
+    gcry_error_t err;
+    char *savedmessage = context->lastmessage;
+    int savedmay_retransmit = context->may_retransmit;
+    time_t savedtime = context->lastsent;
+    ConnectionState state = context->state;
+    *messagep = NULL;
+    context->lastmessage = NULL;
+
+    switch(state) {
+	case CONN_CONNECTED:
+	    if (kem->is_reply == 0) {
+		/* Send a Key Exchange message to the correspondent */
+		err = otrl_proto_create_key_exchange(us, messagep, context, 1,
+			create_privkey, create_privkey_data);
+		if (err) return err;
+	    }
+	    if (context->their_keyid > 0 &&
+		    (kem->keyid == context->their_keyid &&
+			!gcry_mpi_cmp(kem->dh_pubkey, context->their_y))
+		    || (kem->keyid == (context->their_keyid - 1) &&
+			!gcry_mpi_cmp(kem->dh_pubkey, context->their_old_y))) {
+		/* We've already seen this key of theirs, so all is
+		 * good. */
+		break;
+	    }
+	    /* It's an entirely different session; our correspondent has
+	     * gone away and come back. */
+	    otrl_context_force_setup(context);
+
+	    /* FALLTHROUGH */
+	case CONN_UNCONNECTED:
+	case CONN_SETUP:
+	    if (state == CONN_UNCONNECTED ||
+		    (state == CONN_SETUP && kem->is_reply == 0)) {
+		/* Send a Key Exchange message to the correspondent */
+		err = otrl_proto_create_key_exchange(us, messagep, context, 1,
+			create_privkey, create_privkey_data);
+		if (err) return err;
+	    }
+	    context->their_keyid = kem->keyid;
+	    gcry_mpi_release(context->their_y);
+	    context->their_y = gcry_mpi_copy(kem->dh_pubkey);
+	    err = otrl_dh_session(&(context->sesskeys[0][0]),
+		    &(context->our_dh_key), context->their_y);
+	    if (err) return err;
+	    err = otrl_dh_session(&(context->sesskeys[1][0]),
+		    &(context->our_old_dh_key), context->their_y);
+	    if (err) return err;
+	    context->state = CONN_CONNECTED;
+	    context->active_fingerprint = fprint;
+	    context->generation++;
+	    context->lastmessage = savedmessage;
+	    context->may_retransmit = savedmay_retransmit;
+	    context->lastsent = savedtime;
+	    break;
+    }
+
+    return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Create an OTR Data message.  Pass the plaintext as msg, and an
+ * optional chain of TLVs.  A newly-allocated string will be returned in
+ * *encmessagep. */
+gcry_error_t otrl_proto_create_data(char **encmessagep, ConnContext *context,
+	const char *msg, OtrlTLV *tlvs)
+{
+    size_t justmsglen = strlen(msg);
+    size_t msglen = justmsglen + 1 + otrl_tlv_seriallen(tlvs);
+    size_t buflen;
+    size_t pubkeylen;
+    unsigned char *buf = NULL;
+    unsigned char *bufp;
+    size_t lenp;
+    DH_sesskeys *sess = &(context->sesskeys[1][0]);
+    gcry_error_t err;
+    size_t reveallen = 20 * context->numsavedkeys;
+    size_t base64len;
+    char *base64buf = NULL;
+    char *msgbuf = NULL;
+    enum gcry_mpi_format format = GCRYMPI_FMT_USG;
+    char *msgdup;
+
+    /* Make sure we're actually supposed to be able to encrypt */
+    if (context->state != CONN_CONNECTED || context->their_keyid == 0) {
+	return gcry_error(GPG_ERR_CONFLICT);
+    }
+
+    /* We need to copy the incoming msg, since it might be an alias for
+     * context->lastmessage, which we'll be freeing soon. */
+    msgdup = gcry_malloc_secure(justmsglen + 1);
+    if (msgdup == NULL) {
+	return gcry_error(GPG_ERR_ENOMEM);
+    }
+    strcpy(msgdup, msg);
+
+    *encmessagep = NULL;
+
+    /* Header, send keyid, recv keyid, counter, msg len, msg
+     * len of revealed mac keys, revealed mac keys, MAC */
+    buflen = 3 + 4 + 4 + 8 + 4 + msglen + 4 + reveallen + 20;
+    gcry_mpi_print(format, NULL, 0, &pubkeylen, context->our_dh_key.pub);
+    buflen += pubkeylen + 4;
+    buf = malloc(buflen);
+    msgbuf = gcry_malloc_secure(msglen);
+    if (buf == NULL || msgbuf == NULL) {
+	free(buf);
+	gcry_free(msgbuf);
+	gcry_free(msgdup);
+	return gcry_error(GPG_ERR_ENOMEM);
+    }
+    memmove(msgbuf, msgdup, justmsglen);
+    msgbuf[justmsglen] = '\0';
+    otrl_tlv_serialize(msgbuf + justmsglen + 1, tlvs);
+    bufp = buf;
+    lenp = buflen;
+    memmove(bufp, "\x00\x01\x03", 3);  /* header */
+    debug_data("Header", bufp, 3);
+    bufp += 3; lenp -= 3;
+    write_int(context->our_keyid-1);                    /* sender keyid */
+    debug_int("Sender keyid", bufp-4);
+    write_int(context->their_keyid);                    /* recipient keyid */
+    debug_int("Recipient keyid", bufp-4);
+
+    write_mpi(context->our_dh_key.pub, pubkeylen, "Y");      /* Y */
+
+    otrl_dh_incctr(sess->sendctr);
+    memmove(bufp, sess->sendctr, 8);      /* Counter (top 8 bytes only) */
+    debug_data("Counter", bufp, 8);
+    bufp += 8; lenp -= 8;
+
+    write_int(msglen);                        /* length of encrypted data */
+    debug_int("Msg len", bufp-4);
+
+    err = gcry_cipher_reset(sess->sendenc);
+    if (err) goto err;
+    err = gcry_cipher_setctr(sess->sendenc, sess->sendctr, 16);
+    if (err) goto err;
+    err = gcry_cipher_encrypt(sess->sendenc, bufp, msglen, msgbuf, msglen);
+    if (err) goto err;                              /* encrypted data */
+    debug_data("Enc data", bufp, msglen);
+    bufp += msglen;
+    lenp -= msglen;
+
+    gcry_md_reset(sess->sendmac);
+    gcry_md_write(sess->sendmac, buf, bufp-buf);
+    memmove(bufp, gcry_md_read(sess->sendmac, GCRY_MD_SHA1), 20);
+    debug_data("MAC", bufp, 20);
+    bufp += 20;                                         /* MAC */
+    lenp -= 20;
+
+    write_int(reveallen);                     /* length of revealed MAC keys */
+    debug_int("Revealed MAC length", bufp-4);
+
+    if (reveallen > 0) {
+	memmove(bufp, context->saved_mac_keys, reveallen);
+	debug_data("Revealed MAC data", bufp, reveallen);
+	bufp += reveallen; lenp -= reveallen;
+	free(context->saved_mac_keys);
+	context->saved_mac_keys = NULL;
+	context->numsavedkeys = 0;
+    }
+
+    assert(lenp == 0);
+
+    /* Make the base64-encoding. */
+    base64len = ((buflen + 2) / 3) * 4;
+    base64buf = malloc(5 + base64len + 1 + 1);
+    if (base64buf == NULL) {
+	err = GPG_ERR_ENOMEM;
+	goto err;
+    }
+    memmove(base64buf, "?OTR:", 5);
+    otrl_base64_encode(base64buf+5, buf, buflen);
+    base64buf[5 + base64len] = '.';
+    base64buf[5 + base64len + 1] = '\0';
+
+    free(buf);
+    gcry_free(msgbuf);
+    *encmessagep = base64buf;
+    gcry_free(context->lastmessage);
+    context->lastmessage = NULL;
+    context->may_retransmit = 0;
+    if (msglen > 0) {
+	const char *prefix = "[resent] ";
+	size_t prefixlen = strlen(prefix);
+	if (!strncmp(prefix, msgdup, prefixlen)) {
+	    /* The prefix is already there.  Don't add it again. */
+	    prefix = "";
+	    prefixlen = 0;
+	}
+	context->lastmessage = gcry_malloc_secure(prefixlen + justmsglen + 1);
+	if (context->lastmessage) {
+	    strcpy(context->lastmessage, prefix);
+	    strcat(context->lastmessage, msgdup);
+	}
+    }
+    gcry_free(msgdup);
+    return gcry_error(GPG_ERR_NO_ERROR);
+err:
+    free(buf);
+    gcry_free(msgbuf);
+    gcry_free(msgdup);
+    *encmessagep = NULL;
+    return err;
+}
+
+/* Accept an OTR Data Message in datamsg.  Decrypt it and put the
+ * plaintext into *plaintextp, and any TLVs into tlvsp. */
+gcry_error_t otrl_proto_accept_data(char **plaintextp, OtrlTLV **tlvsp,
+	ConnContext *context, const char *datamsg)
+{
+    char *otrtag, *endtag;
+    gcry_error_t err;
+    unsigned char *rawmsg = NULL;
+    size_t msglen, rawlen, lenp;
+    unsigned char *macstart, *macend;
+    unsigned char *bufp;
+    unsigned int sender_keyid, recipient_keyid;
+    gcry_mpi_t sender_next_y = NULL;
+    unsigned char ctr[8];
+    unsigned int datalen, reveallen;
+    unsigned char *data = NULL;
+    unsigned char *nul = NULL;
+    unsigned char givenmac[20];
+    DH_sesskeys *sess;
+
+    *plaintextp = NULL;
+    *tlvsp = NULL;
+    otrtag = strstr(datamsg, "?OTR:");
+    if (!otrtag) {
+	goto invval;
+    }
+    endtag = strchr(otrtag, '.');
+    if (endtag) {
+	msglen = endtag-otrtag;
+    } else {
+	msglen = strlen(otrtag);
+    }
+
+    /* Base64-decode the message */
+    rawlen = ((msglen-5) / 4) * 3;   /* maximum possible */
+    rawmsg = malloc(rawlen);
+    if (!rawmsg && rawlen > 0) {
+	err = gcry_error(GPG_ERR_ENOMEM);
+	goto err;
+    }
+    rawlen = otrl_base64_decode(rawmsg, otrtag+5, msglen-5);  /* actual size */
+
+    bufp = rawmsg;
+    lenp = rawlen;
+
+    macstart = bufp;
+    require_len(3);
+    if (memcmp(bufp, "\x00\x01\x03", 3)) {
+	/* Invalid header */
+	goto invval;
+    }
+    bufp += 3; lenp -= 3;
+
+    read_int(sender_keyid);
+    read_int(recipient_keyid);
+    read_mpi(sender_next_y);
+    require_len(8);
+    memmove(ctr, bufp, 8);
+    bufp += 8; lenp -= 8;
+    read_int(datalen);
+    require_len(datalen);
+    data = malloc(datalen+1);
+    if (!data) {
+	err = gcry_error(GPG_ERR_ENOMEM);
+	goto err;
+    }
+    memmove(data, bufp, datalen);
+    data[datalen] = '\0';
+    bufp += datalen; lenp -= datalen;
+    macend = bufp;
+    require_len(20);
+    memmove(givenmac, bufp, 20);
+    bufp += 20; lenp -= 20;
+    read_int(reveallen);
+    require_len(reveallen);
+    /* Just skip over the revealed MAC keys, which we don't need.  They
+     * were published for deniability of transcripts. */
+    bufp += reveallen; lenp -= reveallen;
+
+    /* That should be everything */
+    if (lenp != 0) goto invval;
+
+    /* We don't take any action on this message (especially rotating
+     * keys) until we've verified the MAC on this message.  To that end,
+     * we need to know which keys this message is claiming to use. */
+    if (context->their_keyid == 0 ||
+	    (sender_keyid != context->their_keyid &&
+		sender_keyid != context->their_keyid - 1) ||
+	    (recipient_keyid != context->our_keyid &&
+	     recipient_keyid != context->our_keyid - 1) ||
+	    sender_keyid == 0 || recipient_keyid == 0) {
+	goto conflict;
+    }
+
+    if (sender_keyid == context->their_keyid - 1 &&
+	    context->their_old_y == NULL) {
+	goto conflict;
+    }
+
+    /* These are the session keys this message is claiming to use. */
+    sess = &(context->sesskeys
+	    [context->our_keyid - recipient_keyid]
+	    [context->their_keyid - sender_keyid]);
+
+    gcry_md_reset(sess->rcvmac);
+    gcry_md_write(sess->rcvmac, macstart, macend-macstart);
+    if (memcmp(givenmac, gcry_md_read(sess->rcvmac, GCRY_MD_SHA1), 20)) {
+	/* The MACs didn't match! */
+	goto conflict;
+    }
+    sess->rcvmacused = 1;
+
+    /* Check to see that the counter is increasing; i.e. that this isn't
+     * a replay. */
+    if (otrl_dh_cmpctr(ctr, sess->rcvctr) <= 0) {
+	goto conflict;
+    }
+
+    /* Decrypt the message */
+    memmove(sess->rcvctr, ctr, 8);
+    err = gcry_cipher_reset(sess->rcvenc);
+    if (err) goto err;
+    err = gcry_cipher_setctr(sess->rcvenc, sess->rcvctr, 16);
+    if (err) goto err;
+    err = gcry_cipher_decrypt(sess->rcvenc, data, datalen, NULL, 0);
+    if (err) goto err;
+
+    /* See if either set of keys needs rotating */
+
+    if (recipient_keyid == context->our_keyid) {
+	/* They're using our most recent key, so generate a new one */
+	err = rotate_dh_keys(context);
+	if (err) goto err;
+    }
+
+    if (sender_keyid == context->their_keyid) {
+	/* They've sent us a new public key */
+	err = rotate_y_keys(context, sender_next_y);
+	if (err) goto err;
+    }
+
+    gcry_mpi_release(sender_next_y);
+    *plaintextp = data;
+
+    /* See if there are TLVs */
+    nul = data;
+    while (nul < data+datalen && *nul) ++nul;
+    /* If we stopped before the end, skip the NUL we stopped at */
+    if (nul < data+datalen) ++nul;
+    *tlvsp = otrl_tlv_parse(nul, (data+datalen)-nul);
+
+    free(rawmsg);
+    return gcry_error(GPG_ERR_NO_ERROR);
+
+invval:
+    err = gcry_error(GPG_ERR_INV_VALUE);
+    goto err;
+conflict:
+    err = gcry_error(GPG_ERR_CONFLICT);
+    goto err;
+err:
+    gcry_mpi_release(sender_next_y);
+    free(data);
+    free(rawmsg);
+    return err;
+}
diff --git a/src/proto.h b/src/proto.h
new file mode 100644
index 0000000..546b6a4
--- /dev/null
+++ b/src/proto.h
@@ -0,0 +1,133 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <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 __PROTO_H__
+#define __PROTO_H__
+
+#include "context.h"
+#include "version.h"
+#include "tlv.h"
+
+/* If we ever see this sequence in a plaintext message, we'll assume the
+ * other side speaks OTR, and try to establish a connection. */
+#define OTR_MESSAGE_TAG " \t  \t\t\t\t \t \t \t   \t \t  \t "
+    /* This is the bit sequence of the string "OTR", encoded in tabs and
+     * spaces. */
+
+typedef enum {
+    OTR_NOTOTR,
+    OTR_TAGGEDPLAINTEXT,
+    OTR_QUERY,
+    OTR_KEYEXCH,
+    OTR_DATA,
+    OTR_ERROR,
+    OTR_UNKNOWN
+} OTRMessageType;
+
+typedef struct s_OTRKeyExchangeMsg {
+    gcry_sexp_t digest_sexp;              /* SHA-1 hash of the raw message,
+					     except for the DSA sig; used
+					     for checking the sig */
+    unsigned char is_reply;               /* Was this a reply to a Key
+					     Exchange Message we sent
+					     them? */
+    unsigned char key_fingerprint[20];    /* The key fingerprint */
+    gcry_sexp_t dsa_pubkey;               /* DSA public key */
+    unsigned int keyid;                   /* DH key id */
+    gcry_mpi_t dh_pubkey;                 /* DH public key */
+    gcry_sexp_t dsa_sig;                  /* Signature on packet */
+} * OTRKeyExchangeMsg;
+
+/* 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);
+
+/* Shortcut */
+#define OTRL_INIT do { \
+	otrl_init(OTRL_VERSION_MAJOR, OTRL_VERSION_MINOR, OTRL_VERSION_SUB); \
+    } while(0)
+
+/* Return a pointer to a static string containing the version number of
+ * the OTR library. */
+const char *otrl_version(void);
+
+/* Create a public key block from a private key */
+gcry_error_t otrl_proto_make_pubkey(unsigned char **pubbufp, size_t *publenp,
+	gcry_sexp_t privkey);
+
+/* Return a pointer to a newly-allocated OTR query message, customized
+ * with our name.  The caller should free() the result when he's done
+ * with it. */
+char *otrl_proto_default_query_msg(const char *ourname);
+
+/* Return the Message type of the given message. */
+OTRMessageType otrl_proto_message_type(const char *message);
+
+/* Create a Key Exchange message for our correspondent.  If we need a
+ * private key and don't have one, create_privkey will be called.  Use
+ * the privkeys from the given OtrlUserState. */
+gcry_error_t otrl_proto_create_key_exchange(OtrlUserState us,
+	char **messagep, ConnContext *context, unsigned char is_reply,
+	void (*create_privkey)(void *create_privkey_data,
+	    const char *accountname, const char *protocol),
+	void *create_privkey_data);
+
+/* Deallocate an OTRKeyExchangeMsg returned from proto_parse_key_exchange */
+void otrl_proto_free_key_exchange(OTRKeyExchangeMsg kem);
+
+/* Parse a purported Key Exchange message.  Possible error code portions
+ * of the return value:
+ *   GPG_ERR_NO_ERROR:      Success
+ *   GPG_ERR_ENOMEM:        Out of memory condition
+ *   GPG_ERR_INV_VALUE:     The message was not a well-formed Key Exchange
+ *                          message
+ *   GPG_ERR_BAD_SIGNATURE: The signature on the message didn't verify
+ */
+gcry_error_t otrl_proto_parse_key_exchange(OTRKeyExchangeMsg *kemp,
+	const char *msg);
+
+/* Deal with a Key Exchange Message once it's been received and passed
+ * all the validity and UI ("accept this fingerprint?") tests.
+ * context/fprint is the ConnContext and Fingerprint to which it
+ * belongs.  Use the given OtrlUserState to look up any necessary
+ * private keys.  It is the caller's responsibility to
+ * otrl_proto_free_key_exchange(kem) when we're done.  If *messagep gets
+ * set to non-NULL by this function, then it's a message that needs to
+ * get sent to the correspondent.  If we need a private key and don't
+ * have one, create_privkey will be called. */
+gcry_error_t otrl_proto_accept_key_exchange(OtrlUserState us,
+	ConnContext *context, Fingerprint *fprint, OTRKeyExchangeMsg kem,
+	char **messagep,
+	void (*create_privkey)(void *create_privkey_data,
+	    const char *accountname, const char *protocol),
+	void *create_privkey_data);
+
+/* Create an OTR Data message.  Pass the plaintext as msg, and an
+ * optional chain of TLVs.  A newly-allocated string will be returned in
+ * *encmessagep. */
+gcry_error_t otrl_proto_create_data(char **encmessagep, ConnContext *context,
+	const char *msg, OtrlTLV *tlvs);
+
+/* Accept an OTR Data Message in datamsg.  Decrypt it and put the
+ * plaintext into *plaintextp, and any TLVs into tlvsp. */
+gcry_error_t otrl_proto_accept_data(char **plaintextp, OtrlTLV **tlvsp,
+	ConnContext *context, const char *datamsg);
+
+#endif
diff --git a/src/tlv.c b/src/tlv.c
new file mode 100644
index 0000000..3da549f
--- /dev/null
+++ b/src/tlv.c
@@ -0,0 +1,107 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <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
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "tlv.h"
+
+/* Make a single TLV, copying the supplied data */
+OtrlTLV *otrl_tlv_new(unsigned short type, unsigned short len,
+	unsigned char *data)
+{
+    OtrlTLV *tlv = malloc(sizeof(OtrlTLV));
+    assert(tlv != NULL);
+    tlv->type = type;
+    tlv->len = len;
+    tlv->data = malloc(len + 1);
+    assert(tlv->data != NULL);
+    memmove(tlv->data, data, len);
+    tlv->data[tlv->len] = '\0';
+    tlv->next = NULL;
+    return tlv;
+}
+
+/* Construct a chain of TLVs from the given data */
+OtrlTLV *otrl_tlv_parse(unsigned char *serialized, size_t seriallen)
+{
+    OtrlTLV *tlv = NULL;
+    OtrlTLV **tlvp = &tlv;
+    while (seriallen >= 4) {
+	unsigned short type = (serialized[0] << 8) + serialized[1];
+	unsigned short len = (serialized[2] << 8) + serialized[3];
+	serialized += 4; seriallen -=4;
+	if (seriallen < len) break;
+	*tlvp = otrl_tlv_new(type, len, serialized);
+	serialized += len;
+	seriallen -= len;
+	tlvp = &((*tlvp)->next);
+    }
+    return tlv;
+}
+
+/* Deallocate a chain of TLVs */
+void otrl_tlv_free(OtrlTLV *tlv)
+{
+    while (tlv) {
+	OtrlTLV *next = tlv->next;
+	free(tlv->data);
+	free(tlv);
+	tlv = next;
+    }
+}
+
+/* Find the serialized length of a chain of TLVs */
+size_t otrl_tlv_seriallen(OtrlTLV *tlv)
+{
+    size_t totlen = 0;
+    while (tlv) {
+	totlen += tlv->len + 4;
+	tlv = tlv->next;
+    }
+    return totlen;
+}
+
+/* Serialize a chain of TLVs.  The supplied buffer must already be large
+ * enough. */
+void otrl_tlv_serialize(unsigned char *buf, OtrlTLV *tlv)
+{
+    while (tlv) {
+	buf[0] = (tlv->type >> 8) & 0xff;
+	buf[1] = tlv->type & 0xff;
+	buf[2] = (tlv->len >> 8) & 0xff;
+	buf[3] = tlv->len & 0xff;
+	buf += 4;
+	memmove(buf, tlv->data, tlv->len);
+	buf += tlv->len;
+	tlv = tlv->next;
+    }
+}
+
+/* Return the first TLV with the given type in the chain, or NULL if one
+ * isn't found.*/
+OtrlTLV *otrl_tlv_find(OtrlTLV *tlvs, unsigned short type)
+{
+    while (tlvs) {
+	if (tlvs->type == type) return tlvs;
+	tlvs = tlvs->next;
+    }
+    return NULL;
+}
diff --git a/src/tlv.h b/src/tlv.h
new file mode 100644
index 0000000..dbbdc51
--- /dev/null
+++ b/src/tlv.h
@@ -0,0 +1,60 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <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 __TLV_H__
+#define __TLV_H__
+
+typedef struct s_OtrlTLV {
+    unsigned short type;
+    unsigned short len;
+    unsigned char *data;
+    struct s_OtrlTLV *next;
+} OtrlTLV;
+
+/* TLV types */
+
+/* This is just padding for the encrypted message, and should be ignored. */
+#define OTRL_TLV_PADDING         0x0000
+
+/* The sender has thrown away his OTR session keys with you */
+#define OTRL_TLV_DISCONNECTED    0x0001
+
+
+/* Make a single TLV, copying the supplied data */
+OtrlTLV *otrl_tlv_new(unsigned short type, unsigned short len,
+	unsigned char *data);
+
+/* Construct a chain of TLVs from the given data */
+OtrlTLV *otrl_tlv_parse(unsigned char *serialized, size_t seriallen);
+
+/* Deallocate a chain of TLVs */
+void otrl_tlv_free(OtrlTLV *tlv);
+
+/* Find the serialized length of a chain of TLVs */
+size_t otrl_tlv_seriallen(OtrlTLV *tlv);
+
+/* Serialize a chain of TLVs.  The supplied buffer must already be large
+ * enough. */
+void otrl_tlv_serialize(unsigned char *buf, OtrlTLV *tlv);
+
+/* Return the first TLV with the given type in the chain, or NULL if one
+ * isn't found.*/
+OtrlTLV *otrl_tlv_find(OtrlTLV *tlvs, unsigned short type);
+
+#endif
diff --git a/src/userstate.c b/src/userstate.c
new file mode 100644
index 0000000..c6f6cf3
--- /dev/null
+++ b/src/userstate.c
@@ -0,0 +1,51 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <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>
+
+/* libotr headers */
+#include "context.h"
+#include "privkey.h"
+#include "userstate.h"
+
+/* Create a new OtrlUserState.  Most clients will only need one of
+ * these.  A OtrlUserState encapsulates the list of known fingerprints
+ * and the list of private keys; if you have separate files for these
+ * things for (say) different users, use different OtrlUserStates.  If
+ * you've got only one user, with multiple accounts all stored together
+ * in the same fingerprint store and privkey store files, use just one
+ * OtrlUserState. */
+OtrlUserState otrl_userstate_create(void)
+{
+    OtrlUserState us = malloc(sizeof(struct s_OtrlUserState));
+    if (!us) return NULL;
+    us->context_root = NULL;
+    us->privkey_root = NULL;
+
+    return us;
+}
+
+/* Free a OtrlUserState */
+void otrl_userstate_free(OtrlUserState us)
+{
+    otrl_context_forget_all(us);
+    otrl_privkey_forget_all(us);
+    free(us);
+}
diff --git a/src/userstate.h b/src/userstate.h
new file mode 100644
index 0000000..31df2f4
--- /dev/null
+++ b/src/userstate.h
@@ -0,0 +1,45 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <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 __USERSTATE_H__
+#define __USERSTATE_H__
+
+typedef struct s_OtrlUserState* OtrlUserState;
+
+#include "context.h"
+#include "privkey.h"
+
+struct s_OtrlUserState {
+    ConnContext *context_root;
+    PrivKey *privkey_root;
+};
+
+/* Create a new OtrlUserState.  Most clients will only need one of
+ * these.  A OtrlUserState encapsulates the list of known fingerprints
+ * and the list of private keys; if you have separate files for these
+ * things for (say) different users, use different OtrlUserStates.  If
+ * you've got only one user, with multiple accounts all stored together
+ * in the same fingerprint store and privkey store files, use just one
+ * OtrlUserState. */
+OtrlUserState otrl_userstate_create(void);
+
+/* Free a OtrlUserState */
+void otrl_userstate_free(OtrlUserState us);
+
+#endif
diff --git a/src/version.h b/src/version.h
new file mode 100644
index 0000000..1096bbb
--- /dev/null
+++ b/src/version.h
@@ -0,0 +1,29 @@
+/*
+ *  Off-the-Record Messaging library
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <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 __VERSION_H__
+#define __VERSION_H__
+
+#define OTRL_VERSION "2.0.2"
+
+#define OTRL_VERSION_MAJOR 2
+#define OTRL_VERSION_MINOR 0
+#define OTRL_VERSION_SUB 2
+
+#endif
diff --git a/toolkit/Makefile.am b/toolkit/Makefile.am
new file mode 100644
index 0000000..1cdb37b
--- /dev/null
+++ b/toolkit/Makefile.am
@@ -0,0 +1,44 @@
+INCLUDES = -I$(includedir) -I../src @LIBGCRYPT_CFLAGS@
+
+noinst_HEADERS = aes.h ctrmode.h parse.h sesskeys.h readotr.h sha1hmac.h
+
+bin_PROGRAMS = otr_parse otr_sesskeys otr_mackey otr_readforge \
+	otr_modify otr_remac
+
+COMMON_S = parse.c sha1hmac.c
+COMMON_LD = ../src/libotr.la @LIBS@ @LIBGCRYPT_LIBS@
+
+otr_parse_SOURCES = otr_parse.c readotr.c $(COMMON_S)
+otr_parse_LDADD = $(COMMON_LD)
+
+otr_sesskeys_SOURCES = otr_sesskeys.c sesskeys.c $(COMMON_S)
+otr_sesskeys_LDADD = $(COMMON_LD)
+
+otr_mackey_SOURCES = otr_mackey.c sesskeys.c $(COMMON_S)
+otr_mackey_LDADD = $(COMMON_LD)
+
+otr_readforge_SOURCES = otr_readforge.c readotr.c sesskeys.c \
+	aes.c ctrmode.c $(COMMON_S)
+otr_readforge_LDADD = $(COMMON_LD)
+
+otr_modify_SOURCES = otr_modify.c readotr.c $(COMMON_S)
+otr_modify_LDADD = $(COMMON_LD)
+
+otr_remac_SOURCES = otr_remac.c $(COMMON_S)
+otr_remac_LDADD = $(COMMON_LD)
+
+
+man_MANS = otr_toolkit.1
+EXTRA_DIST = otr_toolkit.1
+
+MANLINKS = otr_parse.1 otr_sesskeys.1 otr_mackey.1 otr_readforge.1 \
+	    otr_modify.1 otr_remac.1
+	    
+install-data-local:
+	-mkdir -p $(DESTDIR)$(man1dir)
+	(cd $(DESTDIR)$(man1dir) && \
+	for f in $(MANLINKS); do ln -sf otr_toolkit.1 $$f; done)
+
+uninstall-local:
+	(cd $(DESTDIR)$(man1dir) && \
+	for f in $(MANLINKS); do rm -f $$f; done)
diff --git a/toolkit/aes.c b/toolkit/aes.c
new file mode 100644
index 0000000..24b9b56
--- /dev/null
+++ b/toolkit/aes.c
@@ -0,0 +1,866 @@
+/* Retrieved from http://www.cr0.net:8040/code/crypto/aes/aes.c */
+
+/*
+ *  FIPS-197 compliant AES implementation
+ *
+ *  Copyright (C) 2001-2004  Christophe Devine
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "aes.h"
+
+/* uncomment the following line to run the test suite */
+
+/* #define TEST */
+
+/* uncomment the following line to use pre-computed tables */
+/* otherwise the tables will be generated at the first run */
+
+/* #define FIXED_TABLES */
+
+#ifndef FIXED_TABLES
+
+/* forward S-box & tables */
+
+uint32 FSb[256];
+uint32 FT0[256]; 
+uint32 FT1[256]; 
+uint32 FT2[256]; 
+uint32 FT3[256]; 
+
+/* reverse S-box & tables */
+
+uint32 RSb[256];
+uint32 RT0[256];
+uint32 RT1[256];
+uint32 RT2[256];
+uint32 RT3[256];
+
+/* round constants */
+
+uint32 RCON[10];
+
+/* tables generation flag */
+
+int do_init = 1;
+
+/* tables generation routine */
+
+#define ROTR8(x) ( ( ( x << 24 ) & 0xFFFFFFFF ) | \
+                   ( ( x & 0xFFFFFFFF ) >>  8 ) )
+
+#define XTIME(x) ( ( x <<  1 ) ^ ( ( x & 0x80 ) ? 0x1B : 0x00 ) )
+#define MUL(x,y) ( ( x &&  y ) ? pow[(log[x] + log[y]) % 255] : 0 )
+
+void aes_gen_tables( void )
+{
+    int i;
+    uint8 x, y;
+    uint8 pow[256];
+    uint8 log[256];
+
+    /* compute pow and log tables over GF(2^8) */
+
+    for( i = 0, x = 1; i < 256; i++, x ^= XTIME( x ) )
+    {
+        pow[i] = x;
+        log[x] = i;
+    }
+
+    /* calculate the round constants */
+
+    for( i = 0, x = 1; i < 10; i++, x = XTIME( x ) )
+    {
+        RCON[i] = (uint32) x << 24;
+    }
+
+    /* generate the forward and reverse S-boxes */
+
+    FSb[0x00] = 0x63;
+    RSb[0x63] = 0x00;
+
+    for( i = 1; i < 256; i++ )
+    {
+        x = pow[255 - log[i]];
+
+        y = x;  y = ( y << 1 ) | ( y >> 7 );
+        x ^= y; y = ( y << 1 ) | ( y >> 7 );
+        x ^= y; y = ( y << 1 ) | ( y >> 7 );
+        x ^= y; y = ( y << 1 ) | ( y >> 7 );
+        x ^= y ^ 0x63;
+
+        FSb[i] = x;
+        RSb[x] = i;
+    }
+
+    /* generate the forward and reverse tables */
+
+    for( i = 0; i < 256; i++ )
+    {
+        x = (unsigned char) FSb[i]; y = XTIME( x );
+
+        FT0[i] =   (uint32) ( x ^ y ) ^
+                 ( (uint32) x <<  8 ) ^
+                 ( (uint32) x << 16 ) ^
+                 ( (uint32) y << 24 );
+
+        FT0[i] &= 0xFFFFFFFF;
+
+        FT1[i] = ROTR8( FT0[i] );
+        FT2[i] = ROTR8( FT1[i] );
+        FT3[i] = ROTR8( FT2[i] );
+
+        y = (unsigned char) RSb[i];
+
+        RT0[i] = ( (uint32) MUL( 0x0B, y )       ) ^
+                 ( (uint32) MUL( 0x0D, y ) <<  8 ) ^
+                 ( (uint32) MUL( 0x09, y ) << 16 ) ^
+                 ( (uint32) MUL( 0x0E, y ) << 24 );
+
+        RT0[i] &= 0xFFFFFFFF;
+
+        RT1[i] = ROTR8( RT0[i] );
+        RT2[i] = ROTR8( RT1[i] );
+        RT3[i] = ROTR8( RT2[i] );
+    }
+}
+
+#else
+
+/* forward S-box */
+
+static const uint32 FSb[256] =
+{
+    0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5,
+    0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
+    0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0,
+    0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
+    0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC,
+    0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
+    0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A,
+    0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
+    0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0,
+    0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
+    0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B,
+    0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
+    0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85,
+    0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
+    0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5,
+    0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
+    0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17,
+    0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
+    0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88,
+    0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
+    0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C,
+    0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
+    0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9,
+    0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
+    0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6,
+    0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
+    0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E,
+    0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
+    0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94,
+    0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
+    0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68,
+    0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
+};
+
+/* forward tables */
+
+#define FT \
+\
+    V(C6,63,63,A5), V(F8,7C,7C,84), V(EE,77,77,99), V(F6,7B,7B,8D), \
+    V(FF,F2,F2,0D), V(D6,6B,6B,BD), V(DE,6F,6F,B1), V(91,C5,C5,54), \
+    V(60,30,30,50), V(02,01,01,03), V(CE,67,67,A9), V(56,2B,2B,7D), \
+    V(E7,FE,FE,19), V(B5,D7,D7,62), V(4D,AB,AB,E6), V(EC,76,76,9A), \
+    V(8F,CA,CA,45), V(1F,82,82,9D), V(89,C9,C9,40), V(FA,7D,7D,87), \
+    V(EF,FA,FA,15), V(B2,59,59,EB), V(8E,47,47,C9), V(FB,F0,F0,0B), \
+    V(41,AD,AD,EC), V(B3,D4,D4,67), V(5F,A2,A2,FD), V(45,AF,AF,EA), \
+    V(23,9C,9C,BF), V(53,A4,A4,F7), V(E4,72,72,96), V(9B,C0,C0,5B), \
+    V(75,B7,B7,C2), V(E1,FD,FD,1C), V(3D,93,93,AE), V(4C,26,26,6A), \
+    V(6C,36,36,5A), V(7E,3F,3F,41), V(F5,F7,F7,02), V(83,CC,CC,4F), \
+    V(68,34,34,5C), V(51,A5,A5,F4), V(D1,E5,E5,34), V(F9,F1,F1,08), \
+    V(E2,71,71,93), V(AB,D8,D8,73), V(62,31,31,53), V(2A,15,15,3F), \
+    V(08,04,04,0C), V(95,C7,C7,52), V(46,23,23,65), V(9D,C3,C3,5E), \
+    V(30,18,18,28), V(37,96,96,A1), V(0A,05,05,0F), V(2F,9A,9A,B5), \
+    V(0E,07,07,09), V(24,12,12,36), V(1B,80,80,9B), V(DF,E2,E2,3D), \
+    V(CD,EB,EB,26), V(4E,27,27,69), V(7F,B2,B2,CD), V(EA,75,75,9F), \
+    V(12,09,09,1B), V(1D,83,83,9E), V(58,2C,2C,74), V(34,1A,1A,2E), \
+    V(36,1B,1B,2D), V(DC,6E,6E,B2), V(B4,5A,5A,EE), V(5B,A0,A0,FB), \
+    V(A4,52,52,F6), V(76,3B,3B,4D), V(B7,D6,D6,61), V(7D,B3,B3,CE), \
+    V(52,29,29,7B), V(DD,E3,E3,3E), V(5E,2F,2F,71), V(13,84,84,97), \
+    V(A6,53,53,F5), V(B9,D1,D1,68), V(00,00,00,00), V(C1,ED,ED,2C), \
+    V(40,20,20,60), V(E3,FC,FC,1F), V(79,B1,B1,C8), V(B6,5B,5B,ED), \
+    V(D4,6A,6A,BE), V(8D,CB,CB,46), V(67,BE,BE,D9), V(72,39,39,4B), \
+    V(94,4A,4A,DE), V(98,4C,4C,D4), V(B0,58,58,E8), V(85,CF,CF,4A), \
+    V(BB,D0,D0,6B), V(C5,EF,EF,2A), V(4F,AA,AA,E5), V(ED,FB,FB,16), \
+    V(86,43,43,C5), V(9A,4D,4D,D7), V(66,33,33,55), V(11,85,85,94), \
+    V(8A,45,45,CF), V(E9,F9,F9,10), V(04,02,02,06), V(FE,7F,7F,81), \
+    V(A0,50,50,F0), V(78,3C,3C,44), V(25,9F,9F,BA), V(4B,A8,A8,E3), \
+    V(A2,51,51,F3), V(5D,A3,A3,FE), V(80,40,40,C0), V(05,8F,8F,8A), \
+    V(3F,92,92,AD), V(21,9D,9D,BC), V(70,38,38,48), V(F1,F5,F5,04), \
+    V(63,BC,BC,DF), V(77,B6,B6,C1), V(AF,DA,DA,75), V(42,21,21,63), \
+    V(20,10,10,30), V(E5,FF,FF,1A), V(FD,F3,F3,0E), V(BF,D2,D2,6D), \
+    V(81,CD,CD,4C), V(18,0C,0C,14), V(26,13,13,35), V(C3,EC,EC,2F), \
+    V(BE,5F,5F,E1), V(35,97,97,A2), V(88,44,44,CC), V(2E,17,17,39), \
+    V(93,C4,C4,57), V(55,A7,A7,F2), V(FC,7E,7E,82), V(7A,3D,3D,47), \
+    V(C8,64,64,AC), V(BA,5D,5D,E7), V(32,19,19,2B), V(E6,73,73,95), \
+    V(C0,60,60,A0), V(19,81,81,98), V(9E,4F,4F,D1), V(A3,DC,DC,7F), \
+    V(44,22,22,66), V(54,2A,2A,7E), V(3B,90,90,AB), V(0B,88,88,83), \
+    V(8C,46,46,CA), V(C7,EE,EE,29), V(6B,B8,B8,D3), V(28,14,14,3C), \
+    V(A7,DE,DE,79), V(BC,5E,5E,E2), V(16,0B,0B,1D), V(AD,DB,DB,76), \
+    V(DB,E0,E0,3B), V(64,32,32,56), V(74,3A,3A,4E), V(14,0A,0A,1E), \
+    V(92,49,49,DB), V(0C,06,06,0A), V(48,24,24,6C), V(B8,5C,5C,E4), \
+    V(9F,C2,C2,5D), V(BD,D3,D3,6E), V(43,AC,AC,EF), V(C4,62,62,A6), \
+    V(39,91,91,A8), V(31,95,95,A4), V(D3,E4,E4,37), V(F2,79,79,8B), \
+    V(D5,E7,E7,32), V(8B,C8,C8,43), V(6E,37,37,59), V(DA,6D,6D,B7), \
+    V(01,8D,8D,8C), V(B1,D5,D5,64), V(9C,4E,4E,D2), V(49,A9,A9,E0), \
+    V(D8,6C,6C,B4), V(AC,56,56,FA), V(F3,F4,F4,07), V(CF,EA,EA,25), \
+    V(CA,65,65,AF), V(F4,7A,7A,8E), V(47,AE,AE,E9), V(10,08,08,18), \
+    V(6F,BA,BA,D5), V(F0,78,78,88), V(4A,25,25,6F), V(5C,2E,2E,72), \
+    V(38,1C,1C,24), V(57,A6,A6,F1), V(73,B4,B4,C7), V(97,C6,C6,51), \
+    V(CB,E8,E8,23), V(A1,DD,DD,7C), V(E8,74,74,9C), V(3E,1F,1F,21), \
+    V(96,4B,4B,DD), V(61,BD,BD,DC), V(0D,8B,8B,86), V(0F,8A,8A,85), \
+    V(E0,70,70,90), V(7C,3E,3E,42), V(71,B5,B5,C4), V(CC,66,66,AA), \
+    V(90,48,48,D8), V(06,03,03,05), V(F7,F6,F6,01), V(1C,0E,0E,12), \
+    V(C2,61,61,A3), V(6A,35,35,5F), V(AE,57,57,F9), V(69,B9,B9,D0), \
+    V(17,86,86,91), V(99,C1,C1,58), V(3A,1D,1D,27), V(27,9E,9E,B9), \
+    V(D9,E1,E1,38), V(EB,F8,F8,13), V(2B,98,98,B3), V(22,11,11,33), \
+    V(D2,69,69,BB), V(A9,D9,D9,70), V(07,8E,8E,89), V(33,94,94,A7), \
+    V(2D,9B,9B,B6), V(3C,1E,1E,22), V(15,87,87,92), V(C9,E9,E9,20), \
+    V(87,CE,CE,49), V(AA,55,55,FF), V(50,28,28,78), V(A5,DF,DF,7A), \
+    V(03,8C,8C,8F), V(59,A1,A1,F8), V(09,89,89,80), V(1A,0D,0D,17), \
+    V(65,BF,BF,DA), V(D7,E6,E6,31), V(84,42,42,C6), V(D0,68,68,B8), \
+    V(82,41,41,C3), V(29,99,99,B0), V(5A,2D,2D,77), V(1E,0F,0F,11), \
+    V(7B,B0,B0,CB), V(A8,54,54,FC), V(6D,BB,BB,D6), V(2C,16,16,3A)
+
+#define V(a,b,c,d) 0x##a##b##c##d
+static const uint32 FT0[256] = { FT };
+#undef V
+
+#define V(a,b,c,d) 0x##d##a##b##c
+static const uint32 FT1[256] = { FT };
+#undef V
+
+#define V(a,b,c,d) 0x##c##d##a##b
+static const uint32 FT2[256] = { FT };
+#undef V
+
+#define V(a,b,c,d) 0x##b##c##d##a
+static const uint32 FT3[256] = { FT };
+#undef V
+
+#undef FT
+
+/* reverse S-box */
+
+static const uint32 RSb[256] =
+{
+    0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38,
+    0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
+    0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87,
+    0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
+    0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D,
+    0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
+    0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2,
+    0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
+    0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16,
+    0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
+    0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA,
+    0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
+    0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A,
+    0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
+    0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02,
+    0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
+    0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA,
+    0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
+    0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85,
+    0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
+    0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89,
+    0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
+    0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20,
+    0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
+    0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31,
+    0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
+    0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D,
+    0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
+    0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0,
+    0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
+    0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26,
+    0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
+};
+
+/* reverse tables */
+
+#define RT \
+\
+    V(51,F4,A7,50), V(7E,41,65,53), V(1A,17,A4,C3), V(3A,27,5E,96), \
+    V(3B,AB,6B,CB), V(1F,9D,45,F1), V(AC,FA,58,AB), V(4B,E3,03,93), \
+    V(20,30,FA,55), V(AD,76,6D,F6), V(88,CC,76,91), V(F5,02,4C,25), \
+    V(4F,E5,D7,FC), V(C5,2A,CB,D7), V(26,35,44,80), V(B5,62,A3,8F), \
+    V(DE,B1,5A,49), V(25,BA,1B,67), V(45,EA,0E,98), V(5D,FE,C0,E1), \
+    V(C3,2F,75,02), V(81,4C,F0,12), V(8D,46,97,A3), V(6B,D3,F9,C6), \
+    V(03,8F,5F,E7), V(15,92,9C,95), V(BF,6D,7A,EB), V(95,52,59,DA), \
+    V(D4,BE,83,2D), V(58,74,21,D3), V(49,E0,69,29), V(8E,C9,C8,44), \
+    V(75,C2,89,6A), V(F4,8E,79,78), V(99,58,3E,6B), V(27,B9,71,DD), \
+    V(BE,E1,4F,B6), V(F0,88,AD,17), V(C9,20,AC,66), V(7D,CE,3A,B4), \
+    V(63,DF,4A,18), V(E5,1A,31,82), V(97,51,33,60), V(62,53,7F,45), \
+    V(B1,64,77,E0), V(BB,6B,AE,84), V(FE,81,A0,1C), V(F9,08,2B,94), \
+    V(70,48,68,58), V(8F,45,FD,19), V(94,DE,6C,87), V(52,7B,F8,B7), \
+    V(AB,73,D3,23), V(72,4B,02,E2), V(E3,1F,8F,57), V(66,55,AB,2A), \
+    V(B2,EB,28,07), V(2F,B5,C2,03), V(86,C5,7B,9A), V(D3,37,08,A5), \
+    V(30,28,87,F2), V(23,BF,A5,B2), V(02,03,6A,BA), V(ED,16,82,5C), \
+    V(8A,CF,1C,2B), V(A7,79,B4,92), V(F3,07,F2,F0), V(4E,69,E2,A1), \
+    V(65,DA,F4,CD), V(06,05,BE,D5), V(D1,34,62,1F), V(C4,A6,FE,8A), \
+    V(34,2E,53,9D), V(A2,F3,55,A0), V(05,8A,E1,32), V(A4,F6,EB,75), \
+    V(0B,83,EC,39), V(40,60,EF,AA), V(5E,71,9F,06), V(BD,6E,10,51), \
+    V(3E,21,8A,F9), V(96,DD,06,3D), V(DD,3E,05,AE), V(4D,E6,BD,46), \
+    V(91,54,8D,B5), V(71,C4,5D,05), V(04,06,D4,6F), V(60,50,15,FF), \
+    V(19,98,FB,24), V(D6,BD,E9,97), V(89,40,43,CC), V(67,D9,9E,77), \
+    V(B0,E8,42,BD), V(07,89,8B,88), V(E7,19,5B,38), V(79,C8,EE,DB), \
+    V(A1,7C,0A,47), V(7C,42,0F,E9), V(F8,84,1E,C9), V(00,00,00,00), \
+    V(09,80,86,83), V(32,2B,ED,48), V(1E,11,70,AC), V(6C,5A,72,4E), \
+    V(FD,0E,FF,FB), V(0F,85,38,56), V(3D,AE,D5,1E), V(36,2D,39,27), \
+    V(0A,0F,D9,64), V(68,5C,A6,21), V(9B,5B,54,D1), V(24,36,2E,3A), \
+    V(0C,0A,67,B1), V(93,57,E7,0F), V(B4,EE,96,D2), V(1B,9B,91,9E), \
+    V(80,C0,C5,4F), V(61,DC,20,A2), V(5A,77,4B,69), V(1C,12,1A,16), \
+    V(E2,93,BA,0A), V(C0,A0,2A,E5), V(3C,22,E0,43), V(12,1B,17,1D), \
+    V(0E,09,0D,0B), V(F2,8B,C7,AD), V(2D,B6,A8,B9), V(14,1E,A9,C8), \
+    V(57,F1,19,85), V(AF,75,07,4C), V(EE,99,DD,BB), V(A3,7F,60,FD), \
+    V(F7,01,26,9F), V(5C,72,F5,BC), V(44,66,3B,C5), V(5B,FB,7E,34), \
+    V(8B,43,29,76), V(CB,23,C6,DC), V(B6,ED,FC,68), V(B8,E4,F1,63), \
+    V(D7,31,DC,CA), V(42,63,85,10), V(13,97,22,40), V(84,C6,11,20), \
+    V(85,4A,24,7D), V(D2,BB,3D,F8), V(AE,F9,32,11), V(C7,29,A1,6D), \
+    V(1D,9E,2F,4B), V(DC,B2,30,F3), V(0D,86,52,EC), V(77,C1,E3,D0), \
+    V(2B,B3,16,6C), V(A9,70,B9,99), V(11,94,48,FA), V(47,E9,64,22), \
+    V(A8,FC,8C,C4), V(A0,F0,3F,1A), V(56,7D,2C,D8), V(22,33,90,EF), \
+    V(87,49,4E,C7), V(D9,38,D1,C1), V(8C,CA,A2,FE), V(98,D4,0B,36), \
+    V(A6,F5,81,CF), V(A5,7A,DE,28), V(DA,B7,8E,26), V(3F,AD,BF,A4), \
+    V(2C,3A,9D,E4), V(50,78,92,0D), V(6A,5F,CC,9B), V(54,7E,46,62), \
+    V(F6,8D,13,C2), V(90,D8,B8,E8), V(2E,39,F7,5E), V(82,C3,AF,F5), \
+    V(9F,5D,80,BE), V(69,D0,93,7C), V(6F,D5,2D,A9), V(CF,25,12,B3), \
+    V(C8,AC,99,3B), V(10,18,7D,A7), V(E8,9C,63,6E), V(DB,3B,BB,7B), \
+    V(CD,26,78,09), V(6E,59,18,F4), V(EC,9A,B7,01), V(83,4F,9A,A8), \
+    V(E6,95,6E,65), V(AA,FF,E6,7E), V(21,BC,CF,08), V(EF,15,E8,E6), \
+    V(BA,E7,9B,D9), V(4A,6F,36,CE), V(EA,9F,09,D4), V(29,B0,7C,D6), \
+    V(31,A4,B2,AF), V(2A,3F,23,31), V(C6,A5,94,30), V(35,A2,66,C0), \
+    V(74,4E,BC,37), V(FC,82,CA,A6), V(E0,90,D0,B0), V(33,A7,D8,15), \
+    V(F1,04,98,4A), V(41,EC,DA,F7), V(7F,CD,50,0E), V(17,91,F6,2F), \
+    V(76,4D,D6,8D), V(43,EF,B0,4D), V(CC,AA,4D,54), V(E4,96,04,DF), \
+    V(9E,D1,B5,E3), V(4C,6A,88,1B), V(C1,2C,1F,B8), V(46,65,51,7F), \
+    V(9D,5E,EA,04), V(01,8C,35,5D), V(FA,87,74,73), V(FB,0B,41,2E), \
+    V(B3,67,1D,5A), V(92,DB,D2,52), V(E9,10,56,33), V(6D,D6,47,13), \
+    V(9A,D7,61,8C), V(37,A1,0C,7A), V(59,F8,14,8E), V(EB,13,3C,89), \
+    V(CE,A9,27,EE), V(B7,61,C9,35), V(E1,1C,E5,ED), V(7A,47,B1,3C), \
+    V(9C,D2,DF,59), V(55,F2,73,3F), V(18,14,CE,79), V(73,C7,37,BF), \
+    V(53,F7,CD,EA), V(5F,FD,AA,5B), V(DF,3D,6F,14), V(78,44,DB,86), \
+    V(CA,AF,F3,81), V(B9,68,C4,3E), V(38,24,34,2C), V(C2,A3,40,5F), \
+    V(16,1D,C3,72), V(BC,E2,25,0C), V(28,3C,49,8B), V(FF,0D,95,41), \
+    V(39,A8,01,71), V(08,0C,B3,DE), V(D8,B4,E4,9C), V(64,56,C1,90), \
+    V(7B,CB,84,61), V(D5,32,B6,70), V(48,6C,5C,74), V(D0,B8,57,42)
+
+#define V(a,b,c,d) 0x##a##b##c##d
+static const uint32 RT0[256] = { RT };
+#undef V
+
+#define V(a,b,c,d) 0x##d##a##b##c
+static const uint32 RT1[256] = { RT };
+#undef V
+
+#define V(a,b,c,d) 0x##c##d##a##b
+static const uint32 RT2[256] = { RT };
+#undef V
+
+#define V(a,b,c,d) 0x##b##c##d##a
+static const uint32 RT3[256] = { RT };
+#undef V
+
+#undef RT
+
+/* round constants */
+
+static const uint32 RCON[10] =
+{
+    0x01000000, 0x02000000, 0x04000000, 0x08000000,
+    0x10000000, 0x20000000, 0x40000000, 0x80000000,
+    0x1B000000, 0x36000000
+};
+
+int do_init = 0;
+
+void aes_gen_tables( void )
+{
+}
+
+#endif
+
+/* platform-independant 32-bit integer manipulation macros */
+
+#define GET_UINT32(n,b,i)                       \
+{                                               \
+    (n) = ( (uint32) (b)[(i)    ] << 24 )       \
+        | ( (uint32) (b)[(i) + 1] << 16 )       \
+        | ( (uint32) (b)[(i) + 2] <<  8 )       \
+        | ( (uint32) (b)[(i) + 3]       );      \
+}
+
+#define PUT_UINT32(n,b,i)                       \
+{                                               \
+    (b)[(i)    ] = (uint8) ( (n) >> 24 );       \
+    (b)[(i) + 1] = (uint8) ( (n) >> 16 );       \
+    (b)[(i) + 2] = (uint8) ( (n) >>  8 );       \
+    (b)[(i) + 3] = (uint8) ( (n)       );       \
+}
+
+/* decryption key schedule tables */
+
+int KT_init = 1;
+
+uint32 KT0[256];
+uint32 KT1[256];
+uint32 KT2[256];
+uint32 KT3[256];
+
+/* AES key scheduling routine */
+
+int aes_set_key( aes_context *ctx, uint8 *key, int nbits )
+{
+    int i;
+    uint32 *RK, *SK;
+
+    if( do_init )
+    {
+        aes_gen_tables();
+
+        do_init = 0;
+    }
+
+    switch( nbits )
+    {
+        case 128: ctx->nr = 10; break;
+        case 192: ctx->nr = 12; break;
+        case 256: ctx->nr = 14; break;
+        default : return( 1 );
+    }
+
+    RK = ctx->erk;
+
+    for( i = 0; i < (nbits >> 5); i++ )
+    {
+        GET_UINT32( RK[i], key, i * 4 );
+    }
+
+    /* setup encryption round keys */
+
+    switch( nbits )
+    {
+    case 128:
+
+        for( i = 0; i < 10; i++, RK += 4 )
+        {
+            RK[4]  = RK[0] ^ RCON[i] ^
+                        ( FSb[ (uint8) ( RK[3] >> 16 ) ] << 24 ) ^
+                        ( FSb[ (uint8) ( RK[3] >>  8 ) ] << 16 ) ^
+                        ( FSb[ (uint8) ( RK[3]       ) ] <<  8 ) ^
+                        ( FSb[ (uint8) ( RK[3] >> 24 ) ]       );
+
+            RK[5]  = RK[1] ^ RK[4];
+            RK[6]  = RK[2] ^ RK[5];
+            RK[7]  = RK[3] ^ RK[6];
+        }
+        break;
+
+    case 192:
+
+        for( i = 0; i < 8; i++, RK += 6 )
+        {
+            RK[6]  = RK[0] ^ RCON[i] ^
+                        ( FSb[ (uint8) ( RK[5] >> 16 ) ] << 24 ) ^
+                        ( FSb[ (uint8) ( RK[5] >>  8 ) ] << 16 ) ^
+                        ( FSb[ (uint8) ( RK[5]       ) ] <<  8 ) ^
+                        ( FSb[ (uint8) ( RK[5] >> 24 ) ]       );
+
+            RK[7]  = RK[1] ^ RK[6];
+            RK[8]  = RK[2] ^ RK[7];
+            RK[9]  = RK[3] ^ RK[8];
+            RK[10] = RK[4] ^ RK[9];
+            RK[11] = RK[5] ^ RK[10];
+        }
+        break;
+
+    case 256:
+
+        for( i = 0; i < 7; i++, RK += 8 )
+        {
+            RK[8]  = RK[0] ^ RCON[i] ^
+                        ( FSb[ (uint8) ( RK[7] >> 16 ) ] << 24 ) ^
+                        ( FSb[ (uint8) ( RK[7] >>  8 ) ] << 16 ) ^
+                        ( FSb[ (uint8) ( RK[7]       ) ] <<  8 ) ^
+                        ( FSb[ (uint8) ( RK[7] >> 24 ) ]       );
+
+            RK[9]  = RK[1] ^ RK[8];
+            RK[10] = RK[2] ^ RK[9];
+            RK[11] = RK[3] ^ RK[10];
+
+            RK[12] = RK[4] ^
+                        ( FSb[ (uint8) ( RK[11] >> 24 ) ] << 24 ) ^
+                        ( FSb[ (uint8) ( RK[11] >> 16 ) ] << 16 ) ^
+                        ( FSb[ (uint8) ( RK[11] >>  8 ) ] <<  8 ) ^
+                        ( FSb[ (uint8) ( RK[11]       ) ]       );
+
+            RK[13] = RK[5] ^ RK[12];
+            RK[14] = RK[6] ^ RK[13];
+            RK[15] = RK[7] ^ RK[14];
+        }
+        break;
+    }
+
+    /* setup decryption round keys */
+
+    if( KT_init )
+    {
+        for( i = 0; i < 256; i++ )
+        {
+            KT0[i] = RT0[ FSb[i] ];
+            KT1[i] = RT1[ FSb[i] ];
+            KT2[i] = RT2[ FSb[i] ];
+            KT3[i] = RT3[ FSb[i] ];
+        }
+
+        KT_init = 0;
+    }
+
+    SK = ctx->drk;
+
+    *SK++ = *RK++;
+    *SK++ = *RK++;
+    *SK++ = *RK++;
+    *SK++ = *RK++;
+
+    for( i = 1; i < ctx->nr; i++ )
+    {
+        RK -= 8;
+
+        *SK++ = KT0[ (uint8) ( *RK >> 24 ) ] ^
+                KT1[ (uint8) ( *RK >> 16 ) ] ^
+                KT2[ (uint8) ( *RK >>  8 ) ] ^
+                KT3[ (uint8) ( *RK       ) ]; RK++;
+
+        *SK++ = KT0[ (uint8) ( *RK >> 24 ) ] ^
+                KT1[ (uint8) ( *RK >> 16 ) ] ^
+                KT2[ (uint8) ( *RK >>  8 ) ] ^
+                KT3[ (uint8) ( *RK       ) ]; RK++;
+
+        *SK++ = KT0[ (uint8) ( *RK >> 24 ) ] ^
+                KT1[ (uint8) ( *RK >> 16 ) ] ^
+                KT2[ (uint8) ( *RK >>  8 ) ] ^
+                KT3[ (uint8) ( *RK       ) ]; RK++;
+
+        *SK++ = KT0[ (uint8) ( *RK >> 24 ) ] ^
+                KT1[ (uint8) ( *RK >> 16 ) ] ^
+                KT2[ (uint8) ( *RK >>  8 ) ] ^
+                KT3[ (uint8) ( *RK       ) ]; RK++;
+    }
+
+    RK -= 8;
+
+    *SK++ = *RK++;
+    *SK++ = *RK++;
+    *SK++ = *RK++;
+    *SK++ = *RK++;
+
+    return( 0 );
+}
+
+/* AES 128-bit block encryption routine */
+
+void aes_encrypt( aes_context *ctx, uint8 input[16], uint8 output[16] )
+{
+    uint32 *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3;
+
+    RK = ctx->erk;
+
+    GET_UINT32( X0, input,  0 ); X0 ^= RK[0];
+    GET_UINT32( X1, input,  4 ); X1 ^= RK[1];
+    GET_UINT32( X2, input,  8 ); X2 ^= RK[2];
+    GET_UINT32( X3, input, 12 ); X3 ^= RK[3];
+
+#define AES_FROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3)     \
+{                                               \
+    RK += 4;                                    \
+                                                \
+    X0 = RK[0] ^ FT0[ (uint8) ( Y0 >> 24 ) ] ^  \
+                 FT1[ (uint8) ( Y1 >> 16 ) ] ^  \
+                 FT2[ (uint8) ( Y2 >>  8 ) ] ^  \
+                 FT3[ (uint8) ( Y3       ) ];   \
+                                                \
+    X1 = RK[1] ^ FT0[ (uint8) ( Y1 >> 24 ) ] ^  \
+                 FT1[ (uint8) ( Y2 >> 16 ) ] ^  \
+                 FT2[ (uint8) ( Y3 >>  8 ) ] ^  \
+                 FT3[ (uint8) ( Y0       ) ];   \
+                                                \
+    X2 = RK[2] ^ FT0[ (uint8) ( Y2 >> 24 ) ] ^  \
+                 FT1[ (uint8) ( Y3 >> 16 ) ] ^  \
+                 FT2[ (uint8) ( Y0 >>  8 ) ] ^  \
+                 FT3[ (uint8) ( Y1       ) ];   \
+                                                \
+    X3 = RK[3] ^ FT0[ (uint8) ( Y3 >> 24 ) ] ^  \
+                 FT1[ (uint8) ( Y0 >> 16 ) ] ^  \
+                 FT2[ (uint8) ( Y1 >>  8 ) ] ^  \
+                 FT3[ (uint8) ( Y2       ) ];   \
+}
+
+    AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 );       /* round 1 */
+    AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 );       /* round 2 */
+    AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 );       /* round 3 */
+    AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 );       /* round 4 */
+    AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 );       /* round 5 */
+    AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 );       /* round 6 */
+    AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 );       /* round 7 */
+    AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 );       /* round 8 */
+    AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 );       /* round 9 */
+
+    if( ctx->nr > 10 )
+    {
+        AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 );   /* round 10 */
+        AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 );   /* round 11 */
+    }
+
+    if( ctx->nr > 12 )
+    {
+        AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 );   /* round 12 */
+        AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 );   /* round 13 */
+    }
+
+    /* last round */
+
+    RK += 4;
+
+    X0 = RK[0] ^ ( FSb[ (uint8) ( Y0 >> 24 ) ] << 24 ) ^
+                 ( FSb[ (uint8) ( Y1 >> 16 ) ] << 16 ) ^
+                 ( FSb[ (uint8) ( Y2 >>  8 ) ] <<  8 ) ^
+                 ( FSb[ (uint8) ( Y3       ) ]       );
+
+    X1 = RK[1] ^ ( FSb[ (uint8) ( Y1 >> 24 ) ] << 24 ) ^
+                 ( FSb[ (uint8) ( Y2 >> 16 ) ] << 16 ) ^
+                 ( FSb[ (uint8) ( Y3 >>  8 ) ] <<  8 ) ^
+                 ( FSb[ (uint8) ( Y0       ) ]       );
+
+    X2 = RK[2] ^ ( FSb[ (uint8) ( Y2 >> 24 ) ] << 24 ) ^
+                 ( FSb[ (uint8) ( Y3 >> 16 ) ] << 16 ) ^
+                 ( FSb[ (uint8) ( Y0 >>  8 ) ] <<  8 ) ^
+                 ( FSb[ (uint8) ( Y1       ) ]       );
+
+    X3 = RK[3] ^ ( FSb[ (uint8) ( Y3 >> 24 ) ] << 24 ) ^
+                 ( FSb[ (uint8) ( Y0 >> 16 ) ] << 16 ) ^
+                 ( FSb[ (uint8) ( Y1 >>  8 ) ] <<  8 ) ^
+                 ( FSb[ (uint8) ( Y2       ) ]       );
+
+    PUT_UINT32( X0, output,  0 );
+    PUT_UINT32( X1, output,  4 );
+    PUT_UINT32( X2, output,  8 );
+    PUT_UINT32( X3, output, 12 );
+}
+
+/* AES 128-bit block decryption routine */
+
+void aes_decrypt( aes_context *ctx, uint8 input[16], uint8 output[16] )
+{
+    uint32 *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3;
+
+    RK = ctx->drk;
+
+    GET_UINT32( X0, input,  0 ); X0 ^= RK[0];
+    GET_UINT32( X1, input,  4 ); X1 ^= RK[1];
+    GET_UINT32( X2, input,  8 ); X2 ^= RK[2];
+    GET_UINT32( X3, input, 12 ); X3 ^= RK[3];
+
+#define AES_RROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3)     \
+{                                               \
+    RK += 4;                                    \
+                                                \
+    X0 = RK[0] ^ RT0[ (uint8) ( Y0 >> 24 ) ] ^  \
+                 RT1[ (uint8) ( Y3 >> 16 ) ] ^  \
+                 RT2[ (uint8) ( Y2 >>  8 ) ] ^  \
+                 RT3[ (uint8) ( Y1       ) ];   \
+                                                \
+    X1 = RK[1] ^ RT0[ (uint8) ( Y1 >> 24 ) ] ^  \
+                 RT1[ (uint8) ( Y0 >> 16 ) ] ^  \
+                 RT2[ (uint8) ( Y3 >>  8 ) ] ^  \
+                 RT3[ (uint8) ( Y2       ) ];   \
+                                                \
+    X2 = RK[2] ^ RT0[ (uint8) ( Y2 >> 24 ) ] ^  \
+                 RT1[ (uint8) ( Y1 >> 16 ) ] ^  \
+                 RT2[ (uint8) ( Y0 >>  8 ) ] ^  \
+                 RT3[ (uint8) ( Y3       ) ];   \
+                                                \
+    X3 = RK[3] ^ RT0[ (uint8) ( Y3 >> 24 ) ] ^  \
+                 RT1[ (uint8) ( Y2 >> 16 ) ] ^  \
+                 RT2[ (uint8) ( Y1 >>  8 ) ] ^  \
+                 RT3[ (uint8) ( Y0       ) ];   \
+}
+
+    AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 );       /* round 1 */
+    AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 );       /* round 2 */
+    AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 );       /* round 3 */
+    AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 );       /* round 4 */
+    AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 );       /* round 5 */
+    AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 );       /* round 6 */
+    AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 );       /* round 7 */
+    AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 );       /* round 8 */
+    AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 );       /* round 9 */
+
+    if( ctx->nr > 10 )
+    {
+        AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 );   /* round 10 */
+        AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 );   /* round 11 */
+    }
+
+    if( ctx->nr > 12 )
+    {
+        AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 );   /* round 12 */
+        AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 );   /* round 13 */
+    }
+
+    /* last round */
+
+    RK += 4;
+
+    X0 = RK[0] ^ ( RSb[ (uint8) ( Y0 >> 24 ) ] << 24 ) ^
+                 ( RSb[ (uint8) ( Y3 >> 16 ) ] << 16 ) ^
+                 ( RSb[ (uint8) ( Y2 >>  8 ) ] <<  8 ) ^
+                 ( RSb[ (uint8) ( Y1       ) ]       );
+
+    X1 = RK[1] ^ ( RSb[ (uint8) ( Y1 >> 24 ) ] << 24 ) ^
+                 ( RSb[ (uint8) ( Y0 >> 16 ) ] << 16 ) ^
+                 ( RSb[ (uint8) ( Y3 >>  8 ) ] <<  8 ) ^
+                 ( RSb[ (uint8) ( Y2       ) ]       );
+
+    X2 = RK[2] ^ ( RSb[ (uint8) ( Y2 >> 24 ) ] << 24 ) ^
+                 ( RSb[ (uint8) ( Y1 >> 16 ) ] << 16 ) ^
+                 ( RSb[ (uint8) ( Y0 >>  8 ) ] <<  8 ) ^
+                 ( RSb[ (uint8) ( Y3       ) ]       );
+
+    X3 = RK[3] ^ ( RSb[ (uint8) ( Y3 >> 24 ) ] << 24 ) ^
+                 ( RSb[ (uint8) ( Y2 >> 16 ) ] << 16 ) ^
+                 ( RSb[ (uint8) ( Y1 >>  8 ) ] <<  8 ) ^
+                 ( RSb[ (uint8) ( Y0       ) ]       );
+
+    PUT_UINT32( X0, output,  0 );
+    PUT_UINT32( X1, output,  4 );
+    PUT_UINT32( X2, output,  8 );
+    PUT_UINT32( X3, output, 12 );
+}
+
+#ifdef TEST
+
+#include <string.h>
+#include <stdio.h>
+
+/*
+ * Rijndael Monte Carlo Test: ECB mode
+ * source: NIST - rijndael-vals.zip
+ */
+
+static unsigned char AES_enc_test[3][16] =
+{
+    { 0xA0, 0x43, 0x77, 0xAB, 0xE2, 0x59, 0xB0, 0xD0,
+      0xB5, 0xBA, 0x2D, 0x40, 0xA5, 0x01, 0x97, 0x1B },
+    { 0x4E, 0x46, 0xF8, 0xC5, 0x09, 0x2B, 0x29, 0xE2,
+      0x9A, 0x97, 0x1A, 0x0C, 0xD1, 0xF6, 0x10, 0xFB },
+    { 0x1F, 0x67, 0x63, 0xDF, 0x80, 0x7A, 0x7E, 0x70,
+      0x96, 0x0D, 0x4C, 0xD3, 0x11, 0x8E, 0x60, 0x1A }
+};
+    
+static unsigned char AES_dec_test[3][16] =
+{
+    { 0xF5, 0xBF, 0x8B, 0x37, 0x13, 0x6F, 0x2E, 0x1F,
+      0x6B, 0xEC, 0x6F, 0x57, 0x20, 0x21, 0xE3, 0xBA },
+    { 0xF1, 0xA8, 0x1B, 0x68, 0xF6, 0xE5, 0xA6, 0x27,
+      0x1A, 0x8C, 0xB2, 0x4E, 0x7D, 0x94, 0x91, 0xEF },
+    { 0x4D, 0xE0, 0xC6, 0xDF, 0x7C, 0xB1, 0x69, 0x72,
+      0x84, 0x60, 0x4D, 0x60, 0x27, 0x1B, 0xC5, 0x9A }
+};
+    
+int main( void )
+{
+    int m, n, i, j;
+    aes_context ctx;
+    unsigned char buf[16];
+    unsigned char key[32];
+
+    for( m = 0; m < 2; m++ )
+    {
+        printf( "\n Rijndael Monte Carlo Test (ECB mode) - " );
+
+        if( m == 0 ) printf( "encryption\n\n" );
+        if( m == 1 ) printf( "decryption\n\n" );
+
+        for( n = 0; n < 3; n++ )
+        {
+            printf( " Test %d, key size = %3d bits: ",
+                    n + 1, 128 + n * 64 );
+
+            fflush( stdout );
+
+            memset( buf, 0, 16 );
+            memset( key, 0, 16 + n * 8 );
+
+            for( i = 0; i < 400; i++ )
+            {
+                aes_set_key( &ctx, key, 128 + n * 64 );
+
+                for( j = 0; j < 9999; j++ )
+                {
+                    if( m == 0 ) aes_encrypt( &ctx, buf, buf );
+                    if( m == 1 ) aes_decrypt( &ctx, buf, buf );
+                }
+
+                if( n > 0 )
+                {
+                    for( j = 0; j < (n << 3); j++ )
+                    {
+                        key[j] ^= buf[j + 16 - (n << 3)];
+                    }
+                }
+
+                if( m == 0 ) aes_encrypt( &ctx, buf, buf );
+                if( m == 1 ) aes_decrypt( &ctx, buf, buf );
+
+                for( j = 0; j < 16; j++ )
+                {
+                    key[j + (n << 3)] ^= buf[j];
+                }
+            }
+
+            if( ( m == 0 && memcmp( buf, AES_enc_test[n], 16 ) != 0 ) ||
+                ( m == 1 && memcmp( buf, AES_dec_test[n], 16 ) != 0 ) )
+            {
+                printf( "failed!\n" );
+                return( 1 );
+            }
+
+            printf( "passed.\n" );
+        }
+    }
+
+    printf( "\n" );
+
+    return( 0 );
+}
+
+#endif
+
diff --git a/toolkit/aes.h b/toolkit/aes.h
new file mode 100644
index 0000000..67bd423
--- /dev/null
+++ b/toolkit/aes.h
@@ -0,0 +1,26 @@
+/* Retrieved from http://www.cr0.net:8040/code/crypto/aes/aes.h */
+
+#ifndef _AES_H
+#define _AES_H
+
+#ifndef uint8
+#define uint8  unsigned char
+#endif
+
+#ifndef uint32
+#define uint32 unsigned long int
+#endif
+
+typedef struct
+{
+    uint32 erk[64];     /* encryption round keys */
+    uint32 drk[64];     /* decryption round keys */
+    int nr;             /* number of rounds */
+}
+aes_context;
+
+int  aes_set_key( aes_context *ctx, uint8 *key, int nbits );
+void aes_encrypt( aes_context *ctx, uint8 input[16], uint8 output[16] );
+void aes_decrypt( aes_context *ctx, uint8 input[16], uint8 output[16] );
+
+#endif /* aes.h */
diff --git a/toolkit/ctrmode.c b/toolkit/ctrmode.c
new file mode 100644
index 0000000..f2fa1a2
--- /dev/null
+++ b/toolkit/ctrmode.c
@@ -0,0 +1,60 @@
+/*
+ *  Off-the-Record Messaging Toolkit
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; 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 <string.h>
+
+/* toolkit headers */
+#include "aes.h"
+
+/* 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,
+	unsigned char key[16], unsigned char ctrtop[8])
+{
+    unsigned char ctr[16], encctr[16];
+    aes_context aesc;
+
+    aes_set_key(&aesc, key, 128);
+
+    memmove(ctr, ctrtop, 8);
+    memset(ctr+8, 0, 8);
+
+    while(len > 0) {
+	/* How much to do at a time? */
+	size_t i;
+	size_t amt = len;
+	if (amt > 16) amt = 16;
+	aes_encrypt(&aesc, ctr, encctr);
+	for(i=0;i<amt;++i) {
+	    out[i] = in[i] ^ encctr[i];
+	}
+
+	/* Increment the counter */
+	for (i=16;i>0;--i) {
+	    if (++ctr[i-1] != 0) break;
+	}
+
+	out += amt;
+	in += amt;
+	len -= amt;
+    }
+}
diff --git a/toolkit/ctrmode.h b/toolkit/ctrmode.h
new file mode 100644
index 0000000..b74f98b
--- /dev/null
+++ b/toolkit/ctrmode.h
@@ -0,0 +1,29 @@
+/*
+ *  Off-the-Record Messaging Toolkit
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __CTRMODE_H__
+#define __CTRMODE_H__
+
+/* 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,
+	unsigned char key[16], unsigned char ctrtop[8]);
+
+#endif
diff --git a/toolkit/otr_mackey.c b/toolkit/otr_mackey.c
new file mode 100644
index 0000000..e73c94d
--- /dev/null
+++ b/toolkit/otr_mackey.c
@@ -0,0 +1,65 @@
+/*
+ *  Off-the-Record Messaging Toolkit
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* system headers */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* toolkit headers */
+#include "parse.h"
+#include "sesskeys.h"
+
+static void usage(const char *progname)
+{
+    fprintf(stderr, "Usage: %s aeskey\n"
+"Calculate and display the MAC key derived from a given AES key.\n",
+	progname);
+    exit(1);
+}
+
+int main(int argc, char **argv)
+{
+    unsigned char *argbuf;
+    size_t argbuflen;
+    unsigned char mackey[20];
+
+    if (argc != 2) {
+	usage(argv[0]);
+    }
+
+    argv_to_buf(&argbuf, &argbuflen, argv[1]);
+    /* AES keys are 128 bits long, so check for that */
+    if (!argbuf) {
+	usage(argv[0]);
+    }
+    
+    if (argbuflen != 16) {
+	fprintf(stderr, "The AES key must be 32 hex chars long.\n");
+	usage(argv[0]);
+    }
+
+    sesskeys_make_mac(mackey, argbuf);
+
+    dump_data(stdout, "AES key", argbuf, 16);
+    dump_data(stdout, "MAC key", mackey, 20);
+
+    free(argbuf);
+    fflush(stdout);
+    return 0;
+}
diff --git a/toolkit/otr_modify.c b/toolkit/otr_modify.c
new file mode 100644
index 0000000..fe8fe98
--- /dev/null
+++ b/toolkit/otr_modify.c
@@ -0,0 +1,126 @@
+/*
+ *  Off-the-Record Messaging Toolkit
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* system headers */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* libotr headers */
+#include "proto.h"
+
+/* toolkit headers */
+#include "readotr.h"
+#include "parse.h"
+#include "sha1hmac.h"
+
+static void usage(const char *progname)
+{
+    fprintf(stderr, "Usage: %s mackey old_text new_text offset\n"
+"Read an OTR Data Message from stdin.  Even if we can't read the\n"
+"data because we don't know either the AES key or the DH privkey,\n"
+"but we can make a good guess that the substring \"old_text\"\n"
+"appears at the given offset in the message, replace the old_text\n"
+"with the new_text (which must be of the same length), recalculate\n"
+"the MAC with the given mackey, and output the resulting Data message.\n",
+    progname);
+    exit(1);
+}
+
+int main(int argc, char **argv)
+{
+    unsigned char *mackey;
+    size_t mackeylen;
+    unsigned char macval[20];
+    char *otrmsg = NULL;
+    DataMsg datamsg;
+    size_t textlen;
+    unsigned int offset;
+    const unsigned char *old_text, *new_text;
+    char *newdatamsg;
+    size_t i;
+
+    if (argc != 5) {
+	usage(argv[0]);
+    }
+
+    argv_to_buf(&mackey, &mackeylen, argv[1]);
+    if (!mackey) {
+	usage(argv[0]);
+    }
+    
+    if (mackeylen != 20) {
+	fprintf(stderr, "The MAC key must be 40 hex chars long.\n");
+	usage(argv[0]);
+    }
+
+    textlen = strlen(argv[2]);
+    if (textlen != strlen(argv[3])) {
+	fprintf(stderr, "The old_text and new_text must be of the same "
+		"length.\n");
+	usage(argv[0]);
+    }
+    old_text = argv[2];
+    new_text = argv[3];
+
+    if (sscanf(argv[4], "%u", &offset) != 1) {
+	fprintf(stderr, "Unparseable offset given.\n");
+	usage(argv[0]);
+    }
+
+    otrmsg = readotr(stdin);
+    if (otrmsg == NULL) {
+	fprintf(stderr, "No OTR Data Message found on stdin.\n");
+	exit(1);
+    }
+    
+    if (otrl_proto_message_type(otrmsg) != OTR_DATA) {
+	fprintf(stderr, "OTR Non-Data Message found on stdin.\n");
+	exit(1);
+    }
+
+    datamsg = parse_datamsg(otrmsg);
+    free(otrmsg);
+    if (datamsg == NULL) {
+	fprintf(stderr, "Invalid OTR Data Message found on stdin.\n");
+	exit(1);
+    }
+
+    /* Check the MAC */
+    sha1hmac(macval, mackey, datamsg->macstart,
+	    datamsg->macend - datamsg->macstart);
+    if (memcmp(macval, datamsg->mac, 20)) {
+	fprintf(stderr, "MAC does not verify: wrong MAC key?\n");
+	exit(1);
+    }
+
+    /* Modify the ciphertext */
+    for(i=0; i<textlen && offset+i < datamsg->encmsglen; ++i) {
+	datamsg->encmsg[offset+i] ^= (old_text[i] ^ new_text[i]);
+    }
+
+    /* Recalculate the MAC */
+    newdatamsg = remac_datamsg(datamsg, mackey);
+    printf("%s\n", newdatamsg);
+    free(newdatamsg);
+
+    free_datamsg(datamsg);
+    free(mackey);
+    fflush(stdout);
+    return 0;
+}
diff --git a/toolkit/otr_parse.c b/toolkit/otr_parse.c
new file mode 100644
index 0000000..75ff9a8
--- /dev/null
+++ b/toolkit/otr_parse.c
@@ -0,0 +1,129 @@
+/*
+ *  Off-the-Record Messaging Toolkit
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* system headers */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* libotr headers */
+#include "proto.h"
+
+/* toolkit headers */
+#include "readotr.h"
+#include "parse.h"
+
+static void parse(const char *msg)
+{
+    OTRMessageType mtype = otrl_proto_message_type(msg);
+    KeyExchMsg keyexch;
+    DataMsg datamsg;
+
+    switch(mtype) {
+	case OTR_QUERY:
+	    printf("OTR Query:\n\t%s\n\n", msg);
+	    break;
+	case OTR_KEYEXCH:
+	    keyexch = parse_keyexch(msg);
+	    if (!keyexch) {
+		printf("Invalid Key Exchange Message\n\n");
+		break;
+	    }
+	    printf("Key Exchange Message:\n");
+	    dump_int(stdout, "\tReply", keyexch->reply);
+	    dump_mpi(stdout, "\tDSA p", keyexch->p);
+	    dump_mpi(stdout, "\tDSA q", keyexch->q);
+	    dump_mpi(stdout, "\tDSA g", keyexch->g);
+	    dump_mpi(stdout, "\tDSA e", keyexch->e);
+	    dump_int(stdout, "\tKeyID", keyexch->keyid);
+	    dump_mpi(stdout, "\tDH y", keyexch->y);
+	    dump_mpi(stdout, "\tSIG r", keyexch->r);
+	    dump_mpi(stdout, "\tSIG s", keyexch->s);
+	    printf("\n");
+	    free_keyexch(keyexch);
+	    break;
+	case OTR_DATA:
+	    datamsg = parse_datamsg(msg);
+	    if (!datamsg) {
+		printf("Invalid Data Message\n\n");
+		break;
+	    }
+	    printf("Data Message:\n");
+	    dump_int(stdout, "\tSender keyid", datamsg->sender_keyid);
+	    dump_int(stdout, "\tRcpt keyid", datamsg->rcpt_keyid);
+	    dump_mpi(stdout, "\tDH y", datamsg->y);
+	    dump_data(stdout, "\tCounter", datamsg->ctr, 8);
+	    dump_data(stdout, "\tEncrypted message", datamsg->encmsg,
+		    datamsg->encmsglen);
+	    dump_data(stdout, "\tMAC", datamsg->mac, 20);
+	    if (datamsg->mackeyslen > 0) {
+		size_t len = datamsg->mackeyslen;
+		unsigned char *mks = datamsg->mackeys;
+		unsigned int i = 0;
+		printf("\tRevealed MAC keys:\n");
+
+		while(len > 0) {
+		    char title[20];
+		    sprintf(title, "\t\tKey %u", ++i);
+		    dump_data(stdout, title, mks, 20);
+		    mks += 20; len -= 20;
+		}
+	    }
+
+	    printf("\n");
+	    free_datamsg(datamsg);
+	    break;
+	case OTR_ERROR:
+	    printf("OTR Error:\n\t%s\n\n", msg);
+	    break;
+	case OTR_TAGGEDPLAINTEXT:
+	    printf("Tagged plaintext message:\n\t%s\n\n", msg);
+	    break;
+	case OTR_NOTOTR:
+	    printf("Not an OTR message:\n\t%s\n\n", msg);
+	    break;
+	case OTR_UNKNOWN:
+	    printf("Unrecognized OTR message:\n\t%s\n\n", msg);
+	    break;
+    }
+    fflush(stdout);
+}
+
+static void usage(const char *progname)
+{
+    fprintf(stderr, "Usage: %s\n"
+"Read Off-the-Record (OTR) Key Exchange and/or Data messages from stdin\n"
+"and display their contents in a more readable format.\n", progname);
+    exit(1);
+}
+
+int main(int argc, char **argv)
+{
+    char *otrmsg = NULL;
+
+    if (argc != 1) {
+	usage(argv[0]);
+    }
+
+    while ((otrmsg = readotr(stdin)) != NULL) {
+	parse(otrmsg);
+	free(otrmsg);
+    }
+
+    return 0;
+}
diff --git a/toolkit/otr_readforge.c b/toolkit/otr_readforge.c
new file mode 100644
index 0000000..61b98f9
--- /dev/null
+++ b/toolkit/otr_readforge.c
@@ -0,0 +1,132 @@
+/*
+ *  Off-the-Record Messaging Toolkit
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* system headers */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* libotr headers */
+#include "proto.h"
+
+/* toolkit headers */
+#include "readotr.h"
+#include "parse.h"
+#include "sesskeys.h"
+#include "sha1hmac.h"
+#include "ctrmode.h"
+
+static void usage(const char *progname)
+{
+    fprintf(stderr, "Usage: %s aeskey [new_message]\n"
+"Read an OTR Data Message from stdin.  Use the given AES key to\n"
+"verify its MAC and decrypt the message to stdout.  If new_message\n"
+"is given, output a new OTR Data Message with the same fields as the\n"
+"original, but with the message replaced by new_message\n", progname);
+    exit(1);
+}
+
+int main(int argc, char **argv)
+{
+    unsigned char *aeskey;
+    unsigned char mackey[20];
+    unsigned char macval[20];
+    size_t aeskeylen;
+    unsigned char *plaintext, *ciphertext;
+    char *otrmsg = NULL;
+    DataMsg datamsg;
+
+    if (argc != 2 && argc != 3) {
+	usage(argv[0]);
+    }
+
+    argv_to_buf(&aeskey, &aeskeylen, argv[1]);
+    if (!aeskey) {
+	usage(argv[0]);
+    }
+    
+    if (aeskeylen != 16) {
+	fprintf(stderr, "The AES key must be 32 hex chars long.\n");
+	usage(argv[0]);
+    }
+
+    otrmsg = readotr(stdin);
+    if (otrmsg == NULL) {
+	fprintf(stderr, "No OTR Data Message found on stdin.\n");
+	exit(1);
+    }
+    
+    if (otrl_proto_message_type(otrmsg) != OTR_DATA) {
+	fprintf(stderr, "OTR Non-Data Message found on stdin.\n");
+	exit(1);
+    }
+
+    datamsg = parse_datamsg(otrmsg);
+    free(otrmsg);
+    if (datamsg == NULL) {
+	fprintf(stderr, "Invalid OTR Data Message found on stdin.\n");
+	exit(1);
+    }
+
+    /* Create the MAC key */
+    sesskeys_make_mac(mackey, aeskey);
+
+    /* Check the MAC */
+    sha1hmac(macval, mackey, datamsg->macstart,
+	    datamsg->macend - datamsg->macstart);
+    if (memcmp(macval, datamsg->mac, 20)) {
+	fprintf(stderr, "MAC does not verify: wrong AES key?\n");
+    } else {
+	/* Decrypt the message */
+	plaintext = malloc(datamsg->encmsglen+1);
+	if (!plaintext) {
+	    fprintf(stderr, "Out of memory!\n");
+	    exit(1);
+	}
+	aes_ctr_crypt(plaintext, datamsg->encmsg, datamsg->encmsglen,
+		aeskey, datamsg->ctr);
+	plaintext[datamsg->encmsglen] = '\0';
+	printf("Plaintext: ``%s''\n", plaintext);
+	free(plaintext);
+    }
+
+    /* Do we want to forge a message? */
+    if (argv[2] != NULL) {
+	char *newdatamsg;
+	size_t newlen = strlen(argv[2]);
+	ciphertext = malloc(newlen);
+	if (!ciphertext && newlen > 0) {
+	    fprintf(stderr, "Out of memory!\n");
+	    exit(1);
+	}
+	aes_ctr_crypt(ciphertext, argv[2], newlen, aeskey, datamsg->ctr);
+	free(datamsg->encmsg);
+	datamsg->encmsg = ciphertext;
+	datamsg->encmsglen = newlen;
+	
+	newdatamsg = remac_datamsg(datamsg, mackey);
+
+	printf("%s\n", newdatamsg);
+	free(newdatamsg);
+    }
+
+    free_datamsg(datamsg);
+    free(aeskey);
+    fflush(stdout);
+    return 0;
+}
diff --git a/toolkit/otr_remac.c b/toolkit/otr_remac.c
new file mode 100644
index 0000000..e644c4d
--- /dev/null
+++ b/toolkit/otr_remac.c
@@ -0,0 +1,121 @@
+/*
+ *  Off-the-Record Messaging Toolkit
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* system headers */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* libgcrypt headers */
+#include <gcrypt.h>
+
+/* toolkit headers */
+#include "parse.h"
+#include "sha1hmac.h"
+
+static void usage(const char *progname)
+{
+    fprintf(stderr, "Usage: %s mackey snd_keyid rcp_keyid pubkey counter "
+	    "encdata revealed_mackeys\n"
+"Make a new Data message, with the given pieces (note that the\n"
+"data part is already encrypted).  MAC it with the given mackey.\n"
+"mackey, pubkey, counter, encdata, and revealed_mackeys are given\n"
+"as strings of hex chars.  snd_keyid and rcp_keyid are decimal integers.\n", progname);
+    exit(1);
+}
+
+int main(int argc, char **argv)
+{
+    unsigned char *mackey;
+    size_t mackeylen;
+    unsigned int snd_keyid, rcp_keyid;
+    unsigned char *pubkey;
+    size_t pubkeylen;
+    gcry_mpi_t pubv;
+    unsigned char *ctr;
+    size_t ctrlen;
+    unsigned char *encdata;
+    size_t encdatalen;
+    unsigned char *mackeys;
+    size_t mackeyslen;
+    char *newdatamsg;
+
+    if (argc != 8) {
+	usage(argv[0]);
+    }
+
+    argv_to_buf(&mackey, &mackeylen, argv[1]);
+    if (!mackey) {
+	usage(argv[0]);
+    }
+    
+    if (mackeylen != 20) {
+	fprintf(stderr, "The MAC key must be 40 hex chars long.\n");
+	usage(argv[0]);
+    }
+
+    if (sscanf(argv[2], "%u", &snd_keyid) != 1) {
+	fprintf(stderr, "Unparseable snd_keyid given.\n");
+	usage(argv[0]);
+    }
+
+    if (sscanf(argv[3], "%u", &rcp_keyid) != 1) {
+	fprintf(stderr, "Unparseable rcp_keyid given.\n");
+	usage(argv[0]);
+    }
+
+    argv_to_buf(&pubkey, &pubkeylen, argv[4]);
+    if (!pubkey) {
+	usage(argv[0]);
+    }
+    gcry_mpi_scan(&pubv, GCRYMPI_FMT_USG, pubkey, pubkeylen, NULL);
+    free(pubkey);
+    
+    argv_to_buf(&ctr, &ctrlen, argv[5]);
+    if (!ctr) {
+	usage(argv[0]);
+    }
+
+    if (ctrlen != 8) {
+	fprintf(stderr, "The counter must be 16 hex chars long.\n");
+	usage(argv[0]);
+    }
+
+    argv_to_buf(&encdata, &encdatalen, argv[6]);
+    if (!encdata) {
+	usage(argv[0]);
+    }
+
+    argv_to_buf(&mackeys, &mackeyslen, argv[7]);
+    if (!mackeys) {
+	usage(argv[0]);
+    }
+
+    newdatamsg = assemble_datamsg(mackey, snd_keyid, rcp_keyid, pubv, ctr,
+	    encdata, encdatalen, mackeys, mackeyslen);
+    printf("%s\n", newdatamsg);
+    free(newdatamsg);
+
+    free(mackey);
+    gcry_mpi_release(pubv);
+    free(ctr);
+    free(encdata);
+    free(mackeys);
+    fflush(stdout);
+    return 0;
+}
diff --git a/toolkit/otr_sesskeys.c b/toolkit/otr_sesskeys.c
new file mode 100644
index 0000000..f28fd91
--- /dev/null
+++ b/toolkit/otr_sesskeys.c
@@ -0,0 +1,92 @@
+/*
+ *  Off-the-Record Messaging Toolkit
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* system headers */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* toolkit headers */
+#include "parse.h"
+#include "sesskeys.h"
+
+static void usage(const char *progname)
+{
+    fprintf(stderr, "Usage: %s our_privkey their_pubkey\n"
+"Calculate and display our public key, the session id, two AES keys,\n"
+"and two MAC keys generated by the given DH private key and public key.\n",
+	progname);
+    exit(1);
+}
+
+int main(int argc, char **argv)
+{
+    unsigned char *argbuf;
+    size_t argbuflen;
+    gcry_mpi_t our_x, our_y, their_y;
+    unsigned char *pubbuf;
+    size_t publen;
+    unsigned char sessionid[20], sendenc[16], rcvenc[16];
+    unsigned char sendmac[20], rcvmac[20];
+    int is_high;
+
+    if (argc != 3) {
+	usage(argv[0]);
+    }
+
+    argv_to_buf(&argbuf, &argbuflen, argv[1]);
+    /* Private keys are only 320 bits long, so check for that to make
+     * sure they didn't get the args the wrong way around */
+    if (!argbuf || argbuflen > 40) usage(argv[0]);
+    gcry_mpi_scan(&our_x, GCRYMPI_FMT_USG, argbuf, argbuflen, NULL);
+    free(argbuf);
+    argv_to_buf(&argbuf, &argbuflen, argv[2]);
+    if (!argbuf) usage(argv[0]);
+    gcry_mpi_scan(&their_y, GCRYMPI_FMT_USG, argbuf, argbuflen, NULL);
+    free(argbuf);
+
+    sesskeys_gen(sessionid, sendenc, rcvenc, &is_high, &our_y, our_x, their_y);
+    sesskeys_make_mac(sendmac, sendenc);
+    sesskeys_make_mac(rcvmac, rcvenc);
+
+    /* Print our public key into a buffer */
+    gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &publen, our_y);
+    pubbuf = malloc(publen);
+    if (!pubbuf) {
+	fprintf(stderr, "Out of memory!\n");
+	exit(1);
+    }
+    gcry_mpi_print(GCRYMPI_FMT_USG, pubbuf, publen, NULL, our_y);
+
+    puts("");
+    printf("We are the %s end of this key exchange.\n",
+	    is_high ? "high" : "low");
+    puts("");
+    dump_data(stdout, "Our public key", pubbuf, publen);
+    puts("");
+    dump_data(stdout, "Session id", sessionid, 20);
+    puts("");
+    dump_data(stdout, "Sending   AES key", sendenc, 16);
+    dump_data(stdout, "Sending   MAC key", sendmac, 20);
+    dump_data(stdout, "Receiving AES key", rcvenc, 16);
+    dump_data(stdout, "Receiving MAC key", rcvmac, 20);
+    puts("");
+    fflush(stdout);
+
+    return 0;
+}
diff --git a/toolkit/otr_toolkit.1 b/toolkit/otr_toolkit.1
new file mode 100644
index 0000000..d684e58
--- /dev/null
+++ b/toolkit/otr_toolkit.1
@@ -0,0 +1,109 @@
+.\"                                      Hey, EMACS: -*- nroff -*-
+.\" First parameter, NAME, should be all caps
+.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
+.\" other parameters are allowed: see man(7), man(1)
+.TH OTR_PARSE 1 "December  2, 2004"
+.\" Please adjust this date whenever revising the manpage.
+.\"
+.\" Some roff macros, for reference:
+.\" .nh        disable hyphenation
+.\" .hy        enable hyphenation
+.\" .ad l      left justify
+.\" .ad b      justify to both left and right margins
+.\" .nf        disable filling
+.\" .fi        enable filling
+.\" .br        insert line break
+.\" .sp <n>    insert n+1 empty lines
+.\" for manpage-specific macros, see man(7)
+.SH NAME
+otr_parse, otr_sesskeys, otr_mackey, otr_readforge, otr_modify, otr_remac \- Process Off-the-Record Messaging transcripts
+.SH SYNOPSIS
+.B otr_parse
+.br
+.B otr_sesskeys
+.I our_privkey their_pubkey
+.br
+.B otr_mackey
+.I aes_enc_key
+.br
+.B otr_readforge
+.I aes_enc_key [newmsg]
+.br
+.B otr_modify
+.I mackey old_text new_text offset
+.br
+.B otr_remac
+.I mackey snd_keyid rcv_keyid pubkey counter encdata revealed_mackeys
+.SH DESCRIPTION
+Off-the-Record (OTR) Messaging allows you to have private conversations
+over IM by providing:
+ - Encryption
+   - No one else can read your instant messages.
+ - Authentication
+   - You are assured the correspondent is who you think it is.
+ - Deniability
+   - The messages you send do \fInot\fP have digital signatures that are
+     checkable by a third party.  Anyone can forge messages after a
+     conversation to make them look like they came from you.  However,
+     \fIduring\fP a conversation, your correspondent is assured the messages
+     he sees are authentic and unmodified.
+ - Perfect forward secrecy
+   - If you lose control of your private keys, no previous conversation
+     is compromised.
+.PP
+The OTR Toolkit is useful for analyzing and/or
+forging OTR messages.  Why do we offer this?  Primarily, to make
+absolutely sure that transcripts of OTR conversations are really easy
+to forge after the fact.  [Note that \fIduring\fP an OTR conversation,
+messages can't be forged without real-time access to the secret keys on
+the participants' computers, and in that case, all security has already
+been lost.]  Easily-forgeable transcripts help us provide the
+"Deniability" property: if someone claims you said something over OTR,
+they'll have no proof, as anyone at all can modify a transcript to make
+it say whatever they like, and still have all the verification come out
+correctly.
+
+Here are the six programs in the toolkit:
+
+ - otr_parse
+   - Parse OTR messages given on stdin, showing the values of all the
+     fields in OTR Key Exchange Messages and OTR Data Messages.
+
+ - otr_sesskeys our_privkey their_pubkey
+   - Shows our public key, the session id, two AES and two MAC keys
+     derived from the given Diffie-Hellman keys (one private, one public).
+
+ - otr_mackey aes_enc_key
+   - Shows the MAC key derived from the given AES key.
+
+ - otr_readforge aes_enc_key [newmsg]
+   - Decrypts an OTR Data message using the given AES key, and displays
+     the message.
+   - If newmsg is given, replace the message with that one, encrypt
+     and MAC it properly, and output the resulting OTR Data Message.
+     This works even if the given key was not correct for the original
+     message, so as to enable complete forgeries.
+
+ - otr_modify mackey old_text new_text offset
+   - Even if you can't read the data because you don't know either
+     the AES key or the Diffie-Hellman private key, but you can make a
+     good guess that the substring "old_text" appears at the given
+     offset in the message, replace the old_text with the new_text
+     (which must be of the same length), recalculate the MAC with the
+     given mackey, and output the resulting Data message.
+   - Note that, even if you don't know any text in an existing message,
+     you can still forge messages of your choice using the
+     otr_readforge command, above.
+
+ - otr_remac mackey snd_keyid rcv_keyid pubkey counter encdata revealed_mackeys
+   - Make a new OTR Data Message, with the given pieces (note that the
+     data part is already encrypted).  MAC it with the given mackey.
+
+.SH SEE ALSO
+.BR "Off-the-Record Messaging" ,
+at
+.UR http://www.cypherpunks.ca/otr/
+http://www.cypherpunks.ca/otr/
+.UE
+.SH AUTHOR
+otr_toolkit was written by the OTR Dev Team <otr at cypherpunks.ca>.
diff --git a/toolkit/parse.c b/toolkit/parse.c
new file mode 100644
index 0000000..0f58fbb
--- /dev/null
+++ b/toolkit/parse.c
@@ -0,0 +1,379 @@
+/*
+ *  Off-the-Record Messaging Toolkit
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* system headers */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* libotr headers */
+#include "b64.h"
+
+/* toolkit headers */
+#include "sha1hmac.h"
+#include "parse.h"
+
+/* Dump an unsigned int to a FILE * */
+void dump_int(FILE *stream, const char *title, unsigned int val)
+{
+    fprintf(stream, "%s: %u\n", title, val);
+}
+
+/* Dump an mpi to a FILE * */
+void dump_mpi(FILE *stream, const char *title, gcry_mpi_t val)
+{
+    size_t plen;
+    char *d;
+    
+    gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &plen, val);
+    d = malloc(plen);
+    gcry_mpi_print(GCRYMPI_FMT_USG, d, plen, NULL, val);
+    dump_data(stream, title, d, plen);
+    free(d);
+}
+
+/* Dump data to a FILE * */
+void dump_data(FILE *stream, const char *title, const unsigned char *data,
+	size_t datalen)
+{
+    size_t i;
+    fprintf(stream, "%s: ", title);
+    for(i=0;i<datalen;++i) {
+	fprintf(stream, "%02x", data[i]);
+    }
+    fprintf(stream, "\n");
+}
+
+/* base64 decode the message, and put the resulting size into *lenp */
+static unsigned char *decode(const char *msg, size_t *lenp)
+{
+    const char *header, *footer;
+    unsigned char *raw;
+	
+    /* Find the header */
+    header = strstr(msg, "?OTR:");
+    if (!header) return NULL;
+    /* Skip the header */
+    header += 5;
+
+    /* Find the trailing '.' */
+    footer = strchr(header, '.');
+    if (!footer) footer = header + strlen(header);
+
+    raw = malloc((footer-header) / 4 * 3);
+    if (raw == NULL && (footer-header >= 4)) return NULL;
+    *lenp = otrl_base64_decode(raw, header, footer-header);
+
+    return raw;
+}
+
+#define require_len(l) do { if (lenp < (l)) goto inv; } while(0)
+#define read_int(x) do { \
+	require_len(4); \
+	(x) = (bufp[0] << 24) | (bufp[1] << 16) | (bufp[2] << 8 ) | bufp[3]; \
+	bufp += 4; lenp -= 4; \
+    } while(0)
+#define read_mpi(x) do { \
+	size_t mpilen; \
+	read_int(mpilen); \
+	require_len(mpilen); \
+	gcry_mpi_scan(&(x), GCRYMPI_FMT_USG, bufp, mpilen, NULL); \
+	bufp += mpilen; lenp -= mpilen; \
+    } while(0)
+#define read_raw(b, l) do { \
+	require_len(l); \
+	memmove((b), bufp, (l)); \
+	bufp += (l); lenp -= (l); \
+    } while(0)
+#define write_int(x) do { \
+	bufp[0] = ((x) >> 24) & 0xff; \
+	bufp[1] = ((x) >> 16) & 0xff; \
+	bufp[2] = ((x) >> 8) & 0xff; \
+	bufp[3] = (x) & 0xff; \
+	bufp += 4; lenp -= 4; \
+    } while(0)
+#define write_mpi(x,l) do { \
+	write_int(l); \
+	gcry_mpi_print(GCRYMPI_FMT_USG, bufp, lenp, NULL, (x)); \
+	bufp += (l); lenp -= (l); \
+    } while(0)
+#define write_raw(x,l) do { \
+	memmove(bufp, (x), (l)); \
+	bufp += (l); lenp -= (l); \
+    } while(0)
+
+/* Parse a Key Exchange Message into a newly-allocated KeyExchMsg structure */
+KeyExchMsg parse_keyexch(const char *msg)
+{
+    KeyExchMsg kem = NULL;
+    size_t lenp;
+    unsigned char *raw = decode(msg, &lenp);
+    unsigned char *bufp = raw;
+    if (!raw) goto inv;
+
+    kem = calloc(1, sizeof(struct s_KeyExchMsg));
+    if (!kem) {
+	free(raw);
+	goto inv;
+    }
+
+    kem->raw = raw;
+    kem->sigstart = bufp;
+
+    require_len(3);
+    if (memcmp(bufp, "\x00\x01\x0a", 3)) goto inv;
+    bufp += 3; lenp -= 3;
+
+    require_len(1);
+    kem->reply = *bufp;
+    bufp += 1; lenp -= 1;
+
+    read_mpi(kem->p);
+    read_mpi(kem->q);
+    read_mpi(kem->g);
+    read_mpi(kem->e);
+
+    read_int(kem->keyid);
+
+    read_mpi(kem->y);
+
+    kem->sigend = bufp;
+
+    require_len(40);
+    gcry_mpi_scan(&kem->r, GCRYMPI_FMT_USG, bufp, 20, NULL);
+    gcry_mpi_scan(&kem->s, GCRYMPI_FMT_USG, bufp+20, 20, NULL);
+    bufp += 40; lenp -= 40;
+
+    if (lenp != 0) goto inv;
+
+    return kem;
+inv:
+    free_keyexch(kem);
+    return NULL;
+}
+
+/* Deallocate a KeyExchMsg and all of the data it points to */
+void free_keyexch(KeyExchMsg keyexch)
+{
+    if (!keyexch) return;
+    free(keyexch->raw);
+    gcry_mpi_release(keyexch->p);
+    gcry_mpi_release(keyexch->q);
+    gcry_mpi_release(keyexch->g);
+    gcry_mpi_release(keyexch->e);
+    gcry_mpi_release(keyexch->y);
+    gcry_mpi_release(keyexch->r);
+    gcry_mpi_release(keyexch->s);
+    free(keyexch);
+}
+
+/* Parse a Data Message into a newly-allocated DataMsg structure */
+DataMsg parse_datamsg(const char *msg)
+{
+    DataMsg datam = NULL;
+    size_t lenp;
+    unsigned char *raw = decode(msg, &lenp);
+    unsigned char *bufp = raw;
+    if (!raw) goto inv;
+
+    datam = calloc(1, sizeof(struct s_DataMsg));
+    if (!datam) {
+	free(raw);
+	goto inv;
+    }
+
+    datam->raw = raw;
+    datam->rawlen = lenp;
+    datam->macstart = bufp;
+
+    require_len(3);
+    if (memcmp(bufp, "\x00\x01\x03", 3)) goto inv;
+    bufp += 3; lenp -= 3;
+
+    read_int(datam->sender_keyid);
+    read_int(datam->rcpt_keyid);
+    read_mpi(datam->y);
+    read_raw(datam->ctr, 8);
+    read_int(datam->encmsglen);
+    datam->encmsg = malloc(datam->encmsglen);
+    if (!datam->encmsg && datam->encmsglen > 0) goto inv;
+    read_raw(datam->encmsg, datam->encmsglen);
+    datam->macend = bufp;
+    read_raw(datam->mac, 20);
+    read_int(datam->mackeyslen);
+    datam->mackeys = malloc(datam->mackeyslen);
+    if (!datam->mackeys && datam->mackeyslen > 0) goto inv;
+    read_raw(datam->mackeys, datam->mackeyslen);
+
+    if (lenp != 0) goto inv;
+
+    return datam;
+inv:
+    free_datamsg(datam);
+    return NULL;
+}
+
+/* Recalculate the MAC on the message, base64-encode the resulting MAC'd
+ * message, and put on the appropriate header and footer.  Return a
+ * newly-allocated pointer to the result, which the caller will have to
+ * free(). */
+char *remac_datamsg(DataMsg datamsg, unsigned char mackey[20])
+{
+    size_t rawlen, lenp;
+    size_t ylen;
+    size_t base64len;
+    char *outmsg;
+    unsigned char *raw, *bufp;
+    
+    /* Calculate the size of the message that will result */
+    gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &ylen, datamsg->y);
+    rawlen = 3 + 4 + 4 + 4 + ylen + 8 + 4 + datamsg->encmsglen + 20 +
+	4 + datamsg->mackeyslen;
+
+    /* Construct the new raw message (note that some of the pieces may
+     * have been altered, so we construct it from scratch). */
+    raw = malloc(rawlen);
+    if (!raw) {
+	fprintf(stderr, "Out of memory!\n");
+	exit(1);
+    }
+    bufp = raw;
+    lenp = rawlen;
+    datamsg->macstart = raw;
+    datamsg->macend = NULL;
+    free(datamsg->raw);
+    datamsg->raw = raw;
+    datamsg->rawlen = rawlen;
+
+    memmove(bufp, "\x00\x01\x03", 3);
+    bufp += 3; lenp -= 3;
+    write_int(datamsg->sender_keyid);
+    write_int(datamsg->rcpt_keyid);
+    write_mpi(datamsg->y, ylen);
+    write_raw(datamsg->ctr, 8);
+    write_int(datamsg->encmsglen);
+    write_raw(datamsg->encmsg, datamsg->encmsglen);
+    datamsg->macend = bufp;
+
+    /* Recalculate the MAC */
+    sha1hmac(datamsg->mac, mackey, datamsg->macstart,
+	    datamsg->macend - datamsg->macstart);
+
+    write_raw(datamsg->mac, 20);
+    write_int(datamsg->mackeyslen);
+    write_raw(datamsg->mackeys, datamsg->mackeyslen);
+    
+    if (lenp != 0) {
+	fprintf(stderr, "Error creating OTR Data Message.\n");
+	exit(1);
+    }
+
+    base64len = 5 + ((datamsg->rawlen + 2) / 3) * 4 + 1 + 1;
+    outmsg = malloc(base64len);
+    if (!outmsg) return NULL;
+
+    memmove(outmsg, "?OTR:", 5);
+    otrl_base64_encode(outmsg + 5, datamsg->raw, datamsg->rawlen);
+    strcpy(outmsg + base64len - 2, ".");
+    return outmsg;
+}
+
+/* Assemble a new Data Message from its pieces.  Return a
+ * newly-allocated string containing the base64 representation. */
+char *assemble_datamsg(unsigned char mackey[20], unsigned int sender_keyid,
+	unsigned int rcpt_keyid, gcry_mpi_t y, unsigned char ctr[8],
+	unsigned char *encmsg, size_t encmsglen, unsigned char *mackeys,
+	size_t mackeyslen)
+{
+    DataMsg datam = calloc(1, sizeof(struct s_DataMsg));
+    char *newmsg = NULL;
+    if (!datam) goto inv;
+    datam->sender_keyid = sender_keyid;
+    datam->rcpt_keyid = rcpt_keyid;
+    datam->y = gcry_mpi_copy(y);
+    memmove(datam->ctr, ctr, 8);
+    datam->encmsg = malloc(encmsglen);
+    if (!datam->encmsg && encmsglen > 0) goto inv;
+    memmove(datam->encmsg, encmsg, encmsglen);
+    datam->encmsglen = encmsglen;
+    datam->mackeys = malloc(mackeyslen);
+    if (!datam->mackeys && mackeyslen > 0) goto inv;
+    memmove(datam->mackeys, mackeys, mackeyslen);
+    datam->mackeyslen = mackeyslen;
+
+    /* Recalculate the MAC and base64-encode the result */
+    newmsg = remac_datamsg(datam, mackey);
+    free_datamsg(datam);
+    return newmsg;
+inv:
+    free_datamsg(datam);
+    return NULL;
+}
+
+/* Deallocate a DataMsg and all of the data it points to */
+void free_datamsg(DataMsg datamsg)
+{
+    if (!datamsg) return;
+    free(datamsg->raw);
+    gcry_mpi_release(datamsg->y);
+    free(datamsg->encmsg);
+    free(datamsg->mackeys);
+    free(datamsg);
+}
+
+static int ctoh(char c)
+{
+    if (c >= '0' && c <= '9') return (c-'0');
+    if (c >= 'a' && c <= 'f') return (c-'a'+10);
+    if (c >= 'A' && c <= 'F') return (c-'A'+10);
+    return -1;
+}
+
+/* Convert a string of hex chars to a buffer of unsigned chars. */
+void argv_to_buf(unsigned char **bufp, size_t *lenp, char *arg)
+{
+    unsigned char *buf;
+    size_t len, i;
+
+    *bufp = NULL;
+    *lenp = 0;
+
+    len = strlen(arg);
+    if (len % 2) {
+	fprintf(stderr, "Argument ``%s'' must have even length.\n", arg);
+	return;
+    }
+    buf = malloc(len/2);
+    if (buf == NULL && len > 0) {
+	fprintf(stderr, "Out of memory!\n");
+	return;
+    }
+
+    for(i=0;i<len/2;++i) {
+	int hi = ctoh(arg[2*i]);
+	int lo = ctoh(arg[2*i+1]);
+	if (hi < 0 || lo < 0) {
+	    free(buf);
+	    fprintf(stderr, "Illegal hex char in argument ``%s''.\n", arg);
+	    return;
+	}
+	buf[i] = (hi << 4) + lo;
+    }
+    *bufp = buf;
+    *lenp = len/2;
+}
diff --git a/toolkit/parse.h b/toolkit/parse.h
new file mode 100644
index 0000000..f5362d4
--- /dev/null
+++ b/toolkit/parse.h
@@ -0,0 +1,90 @@
+/*
+ *  Off-the-Record Messaging Toolkit
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __PARSE_H__
+#define __PARSE_H__
+
+#include <gcrypt.h>
+
+typedef struct s_KeyExchMsg {
+    unsigned char *raw;         /* The base64-decoded data; must be free()d */
+    unsigned char reply;
+    gcry_mpi_t p, q, g, e;
+    unsigned int keyid;
+    gcry_mpi_t y;
+    gcry_mpi_t r, s;
+    unsigned char *sigstart;    /* Pointers into the "raw" array.  Don't */
+    unsigned char *sigend;      /*   free() these. */
+} * KeyExchMsg;
+
+typedef struct s_DataMsg {
+    unsigned char *raw;         /* The base64-decoded data; must be free()d */
+    size_t rawlen;
+    unsigned int sender_keyid;
+    unsigned int rcpt_keyid;
+    gcry_mpi_t y;
+    unsigned char ctr[8];
+    unsigned char *encmsg;      /* A copy; must be free()d */
+    size_t encmsglen;
+    unsigned char mac[20];
+    unsigned char *mackeys;     /* A copy; must be free()d */
+    size_t mackeyslen;
+    unsigned char *macstart;    /* Pointers into the "raw" array.  Don't */
+    unsigned char *macend;      /*   free() these. */
+} * DataMsg;
+
+/* Dump an unsigned int to a FILE * */
+void dump_int(FILE *stream, const char *title, unsigned int val);
+
+/* Dump an mpi to a FILE * */
+void dump_mpi(FILE *stream, const char *title, gcry_mpi_t val);
+
+/* Dump data to a FILE * */
+void dump_data(FILE *stream, const char *title, const unsigned char *data,
+	size_t datalen);
+
+/* Parse a Key Exchange Message into a newly-allocated KeyExchMsg structure */
+KeyExchMsg parse_keyexch(const char *msg);
+
+/* Deallocate a KeyExchMsg and all of the data it points to */
+void free_keyexch(KeyExchMsg keyexch);
+
+/* Parse a Data Message into a newly-allocated DataMsg structure */
+DataMsg parse_datamsg(const char *msg);
+
+/* Recalculate the MAC on the message, base64-encode the resulting MAC'd
+ * message, and put on the appropriate header and footer.  Return a
+ * newly-allocated pointer to the result, which the caller will have to
+ * free(). */
+char *remac_datamsg(DataMsg datamsg, unsigned char mackey[20]);
+
+/* Assemble a new Data Message from its pieces.  Return a
+ * newly-allocated string containing the base64 representation. */
+char *assemble_datamsg(unsigned char mackey[20], unsigned int sender_keyid,
+	unsigned int rcpt_keyid, gcry_mpi_t y, unsigned char ctr[8],
+	unsigned char *encmsg, size_t encmsglen, unsigned char *mackeys,
+	size_t mackeyslen);
+
+/* Deallocate a DataMsg and all of the data it points to */
+void free_datamsg(DataMsg datamsg);
+
+/* Convert a string of hex chars to a buffer of unsigned chars. */
+void argv_to_buf(unsigned char **bufp, size_t *lenp, char *arg);
+
+#endif
diff --git a/toolkit/readotr.c b/toolkit/readotr.c
new file mode 100644
index 0000000..03927f3
--- /dev/null
+++ b/toolkit/readotr.c
@@ -0,0 +1,91 @@
+/*
+ *  Off-the-Record Messaging Toolkit
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* system headers */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct {
+    char *data;
+    size_t len;
+    size_t alloclen;
+} Buffer;
+
+static void buf_new(Buffer *bufp)
+{
+    bufp->data = NULL;
+    bufp->len = 0;
+    bufp->alloclen = 0;
+}
+
+static void buf_put(Buffer *bufp, const char *str, size_t len)
+{
+    while (bufp->len + len + 1 > bufp->alloclen) {
+	char *newdata = realloc(bufp->data, bufp->alloclen + 1024);
+	if (!newdata) {
+	    fprintf(stderr, "Out of memory!\n");
+	    exit(1);
+	}
+	bufp->data = newdata;
+	bufp->alloclen += 1024;
+    }
+    memmove(bufp->data + bufp->len, str, len);
+    bufp->len += len;
+    bufp->data[bufp->len] = '\0';
+}
+
+static void buf_putc(Buffer *bufp, char c)
+{
+    buf_put(bufp, &c, 1);
+}
+
+/* Read from the given stream until we see a complete OTR Key Exchange
+ * or OTR Data message.  Return a newly-allocated pointer to a copy of
+ * this message, which the caller should free().  Returns NULL if no
+ * such message could be found. */
+char *readotr(FILE *stream)
+{
+    int seen = 0;
+    const char header[] = "?OTR:";  /* There are no '?' chars other than
+				       the leading one */
+    int headerlen = strlen(header);
+    Buffer buf;
+
+    while(seen < headerlen) {
+	int c = fgetc(stream);
+	if (c == EOF) return NULL;
+	else if (c == header[seen]) seen++;
+	else if (c == header[0]) seen = 1;
+	else seen = 0;
+    }
+
+    buf_new(&buf);
+    buf_put(&buf, header, headerlen);
+
+    /* Look for the trailing '.' */
+    while(1) {
+	int c = fgetc(stream);
+	if (c == EOF) break;
+	buf_putc(&buf, c);
+	if (c == '.') break;
+    }
+
+    return buf.data;
+}
diff --git a/toolkit/readotr.h b/toolkit/readotr.h
new file mode 100644
index 0000000..a0c5763
--- /dev/null
+++ b/toolkit/readotr.h
@@ -0,0 +1,29 @@
+/*
+ *  Off-the-Record Messaging Toolkit
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __READOTR_H__
+#define __READOTR_H__
+
+/* Read from the given stream until we see a complete OTR Key Exchange
+ * or OTR Data message.  Return a newly-allocated pointer to a copy of
+ * this message, which the caller should free().  Returns NULL if no
+ * such message could be found. */
+char *readotr(FILE *stream);
+
+#endif
diff --git a/toolkit/sesskeys.c b/toolkit/sesskeys.c
new file mode 100644
index 0000000..17acaa3
--- /dev/null
+++ b/toolkit/sesskeys.c
@@ -0,0 +1,94 @@
+/*
+ *  Off-the-Record Messaging Toolkit
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* system headers */
+#include <stdlib.h>
+
+/* libgcrypt headers */
+#include <gcrypt.h>
+
+static const char* DH1536_MODULUS_S = "0x"
+    "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+    "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+    "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+    "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+    "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+    "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+    "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+    "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF";
+static const char *DH1536_GENERATOR_S = "0x02";
+
+/* Generate the session id and the two encryption keys from our private
+ * DH key and their public DH key.  Also indicate in *high_endp if we
+ * are the "high" end of the key exchange (set to 1) or the "low" end
+ * (set to 0) */
+void sesskeys_gen(unsigned char sessionid[20], unsigned char sendenc[16],
+	unsigned char rcvenc[16], int *high_endp, gcry_mpi_t *our_yp,
+	gcry_mpi_t our_x, gcry_mpi_t their_y)
+{
+    gcry_mpi_t modulus, generator, secretv;
+    unsigned char *secret;
+    size_t secretlen;
+    unsigned char hash[20];
+    int is_high;
+
+    gcry_mpi_scan(&modulus, GCRYMPI_FMT_HEX, DH1536_MODULUS_S, 0, NULL);
+    gcry_mpi_scan(&generator, GCRYMPI_FMT_HEX, DH1536_GENERATOR_S, 0, NULL);
+    *our_yp = gcry_mpi_new(0);
+    gcry_mpi_powm(*our_yp, generator, our_x, modulus);
+    secretv = gcry_mpi_new(0);
+    gcry_mpi_powm(secretv, their_y, our_x, modulus);
+    gcry_mpi_release(generator);
+    gcry_mpi_release(modulus);
+    gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &secretlen, secretv);
+    secret = malloc(secretlen + 5);
+
+    secret[1] = (secretlen >> 24) & 0xff;
+    secret[2] = (secretlen >> 16) & 0xff;
+    secret[3] = (secretlen >> 8) & 0xff;
+    secret[4] = (secretlen) & 0xff;
+    gcry_mpi_print(GCRYMPI_FMT_USG, secret+5, secretlen, NULL, secretv);
+    gcry_mpi_release(secretv);
+
+    is_high = (gcry_mpi_cmp(*our_yp, their_y) > 0);
+
+    /* Calculate the session id */
+    secret[0] = 0x00;
+    gcry_md_hash_buffer(GCRY_MD_SHA1, hash, secret, secretlen+5);
+    memmove(sessionid, hash, 20);
+
+    /* Calculate the sending enc key */
+    secret[0] = is_high ? 0x01 : 0x02;
+    gcry_md_hash_buffer(GCRY_MD_SHA1, hash, secret, secretlen+5);
+    memmove(sendenc, hash, 16);
+
+    /* Calculate the receiving enc key */
+    secret[0] = is_high ? 0x02 : 0x01;
+    gcry_md_hash_buffer(GCRY_MD_SHA1, hash, secret, secretlen+5);
+    memmove(rcvenc, hash, 16);
+
+    *high_endp = is_high;
+    free(secret);
+}
+
+/* Generate a MAC key from the corresponding encryption key */
+void sesskeys_make_mac(unsigned char mackey[20], unsigned char enckey[16])
+{
+    gcry_md_hash_buffer(GCRY_MD_SHA1, mackey, enckey, 16);
+}
diff --git a/toolkit/sesskeys.h b/toolkit/sesskeys.h
new file mode 100644
index 0000000..7a16061
--- /dev/null
+++ b/toolkit/sesskeys.h
@@ -0,0 +1,34 @@
+/*
+ *  Off-the-Record Messaging Toolkit
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __SESSKEYS_H__
+#define __SESSKEYS_H__
+
+/* Generate the session id and the two encryption keys from our private
+ * DH key and their public DH key.  Also indicate in *high_endp if we
+ * are the "high" end of the key exchange (set to 1) or the "low" end
+ * (set to 0) */
+void sesskeys_gen(unsigned char sessionid[20], unsigned char sendenc[16],
+	unsigned char rcvenc[16], int *high_endp, gcry_mpi_t *our_yp,
+	gcry_mpi_t our_x, gcry_mpi_t their_y);
+
+/* Generate a MAC key from the corresponding encryption key */
+void sesskeys_make_mac(unsigned char mackey[20], unsigned char enckey[16]);
+
+#endif
diff --git a/toolkit/sha1hmac.c b/toolkit/sha1hmac.c
new file mode 100644
index 0000000..871337e
--- /dev/null
+++ b/toolkit/sha1hmac.c
@@ -0,0 +1,61 @@
+/*
+ *  Off-the-Record Messaging Toolkit
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* system headers */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* libgcrypt headers */
+#include <gcrypt.h>
+
+/* Implementation of SHA1-HMAC.  We're rolling our own just to
+ * double-check that the calls libotr makes to libgcrypt are in fact
+ * doing the right thing. */
+void sha1hmac(unsigned char digest[20], unsigned char key[20],
+	unsigned char *data, size_t datalen)
+{
+    unsigned char ipad[64], opad[64];
+    size_t i;
+    gcry_md_hd_t sha1;
+    gcry_error_t err;
+    unsigned char hash[20];
+
+    memset(ipad, 0, 64);
+    memset(opad, 0, 64);
+    memmove(ipad, key, 20);
+    memmove(opad, key, 20);
+    for(i=0;i<64;++i) {
+	ipad[i] ^= 0x36;
+	opad[i] ^= 0x5c;
+    }
+
+    err = gcry_md_open(&sha1, GCRY_MD_SHA1, 0);
+    if (err) {
+	fprintf(stderr, "Error: %s\n", gcry_strerror(err));
+	exit(1);
+    }
+    gcry_md_write(sha1, ipad, 64);
+    gcry_md_write(sha1, data, datalen);
+    memmove(hash, gcry_md_read(sha1, 0), 20);
+    gcry_md_reset(sha1);
+    gcry_md_write(sha1, opad, 64);
+    gcry_md_write(sha1, hash, 20);
+    memmove(digest, gcry_md_read(sha1, 0), 20);
+    gcry_md_close(sha1);
+}
diff --git a/toolkit/sha1hmac.h b/toolkit/sha1hmac.h
new file mode 100644
index 0000000..47e57b3
--- /dev/null
+++ b/toolkit/sha1hmac.h
@@ -0,0 +1,29 @@
+/*
+ *  Off-the-Record Messaging Toolkit
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __SHA1HMAC_H__
+#define __SHA1HMAC_H__
+
+/* Implementation of SHA1-HMAC.  We're rolling our own just to
+ * double-check that the calls libotr makes to libgcrypt are in fact
+ * doing the right thing. */
+void sha1hmac(unsigned char digest[20], unsigned char key[20],
+	unsigned char *data, size_t datalen);
+
+#endif

-- 
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