[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