summaryrefslogtreecommitdiff
path: root/libotr-3.2.0/src
diff options
context:
space:
mode:
authoradmin@progandy.co.cc <admin@progandy.co.cc@eced67a3-f377-a0ae-92ae-d6de1850b05a>2010-08-19 20:12:06 +0000
committeradmin@progandy.co.cc <admin@progandy.co.cc@eced67a3-f377-a0ae-92ae-d6de1850b05a>2010-08-19 20:12:06 +0000
commit11e5e8749eb7d4d3cfebfa49bbb7ea4624608647 (patch)
treec773fee94a63a078e5cb59bdbfd3165f1830b7ea /libotr-3.2.0/src
parentc7e64af067562167b6941f3ad8383e3ceb817633 (diff)
initial commit with v0.8.6.1
git-svn-id: http://mirotr.googlecode.com/svn/trunk@2 eced67a3-f377-a0ae-92ae-d6de1850b05a
Diffstat (limited to 'libotr-3.2.0/src')
-rw-r--r--libotr-3.2.0/src/Makefile.am13
-rw-r--r--libotr-3.2.0/src/Makefile.in494
-rw-r--r--libotr-3.2.0/src/auth.c1413
-rw-r--r--libotr-3.2.0/src/auth.h157
-rw-r--r--libotr-3.2.0/src/b64.c249
-rw-r--r--libotr-3.2.0/src/b64.h57
-rw-r--r--libotr-3.2.0/src/context.c330
-rw-r--r--libotr-3.2.0/src/context.h182
-rw-r--r--libotr-3.2.0/src/dh.c466
-rw-r--r--libotr-3.2.0/src/dh.h119
-rw-r--r--libotr-3.2.0/src/mem.c163
-rw-r--r--libotr-3.2.0/src/mem.h25
-rw-r--r--libotr-3.2.0/src/message.c1446
-rw-r--r--libotr-3.2.0/src/message.h210
-rw-r--r--libotr-3.2.0/src/privkey-t.h39
-rw-r--r--libotr-3.2.0/src/privkey.c749
-rw-r--r--libotr-3.2.0/src/privkey.h115
-rw-r--r--libotr-3.2.0/src/proto.c906
-rw-r--r--libotr-3.2.0/src/proto.h150
-rw-r--r--libotr-3.2.0/src/serial.h85
-rw-r--r--libotr-3.2.0/src/sm.c929
-rw-r--r--libotr-3.2.0/src/sm.h83
-rw-r--r--libotr-3.2.0/src/tlv.c108
-rw-r--r--libotr-3.2.0/src/tlv.h70
-rw-r--r--libotr-3.2.0/src/userstate.c51
-rw-r--r--libotr-3.2.0/src/userstate.h45
-rw-r--r--libotr-3.2.0/src/version.h29
27 files changed, 8683 insertions, 0 deletions
diff --git a/libotr-3.2.0/src/Makefile.am b/libotr-3.2.0/src/Makefile.am
new file mode 100644
index 0000000..c75fcbe
--- /dev/null
+++ b/libotr-3.2.0/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 auth.c sm.c
+
+libotr_la_LDFLAGS = -version-info @LIBOTR_LIBTOOL_VERSION@ @LIBS@ @LIBGCRYPT_LIBS@
+
+otrincdir = $(includedir)/libotr
+
+otrinc_HEADERS = b64.h context.h dh.h mem.h message.h privkey.h proto.h \
+ version.h userstate.h tlv.h serial.h auth.h sm.h privkey-t.h
diff --git a/libotr-3.2.0/src/Makefile.in b/libotr-3.2.0/src/Makefile.in
new file mode 100644
index 0000000..5de6fb4
--- /dev/null
+++ b/libotr-3.2.0/src/Makefile.in
@@ -0,0 +1,494 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src
+DIST_COMMON = $(otrinc_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(otrincdir)"
+libLTLIBRARIES_INSTALL = $(INSTALL)
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libotr_la_LIBADD =
+am_libotr_la_OBJECTS = privkey.lo context.lo proto.lo b64.lo dh.lo \
+ mem.lo message.lo userstate.lo tlv.lo auth.lo sm.lo
+libotr_la_OBJECTS = $(am_libotr_la_OBJECTS)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libotr_la_SOURCES)
+DIST_SOURCES = $(libotr_la_SOURCES)
+otrincHEADERS_INSTALL = $(INSTALL_HEADER)
+HEADERS = $(otrinc_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBOTR_LIBTOOL_VERSION = @LIBOTR_LIBTOOL_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+INCLUDES = @LIBGCRYPT_CFLAGS@
+lib_LTLIBRARIES = libotr.la
+libotr_la_SOURCES = privkey.c context.c proto.c b64.c dh.c mem.c message.c \
+ userstate.c tlv.c auth.c sm.c
+
+libotr_la_LDFLAGS = -version-info @LIBOTR_LIBTOOL_VERSION@ @LIBS@ @LIBGCRYPT_LIBS@
+otrincdir = $(includedir)/libotr
+otrinc_HEADERS = b64.h context.h dh.h mem.h message.h privkey.h proto.h \
+ version.h userstate.h tlv.h serial.h auth.h sm.h privkey-t.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ f=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \
+ else :; fi; \
+ done
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @set -x; list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ p=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \
+ $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libotr.la: $(libotr_la_OBJECTS) $(libotr_la_DEPENDENCIES)
+ $(LINK) -rpath $(libdir) $(libotr_la_LDFLAGS) $(libotr_la_OBJECTS) $(libotr_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/b64.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/context.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dh.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mem.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/privkey.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proto.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sm.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tlv.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/userstate.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+install-otrincHEADERS: $(otrinc_HEADERS)
+ @$(NORMAL_INSTALL)
+ test -z "$(otrincdir)" || $(mkdir_p) "$(DESTDIR)$(otrincdir)"
+ @list='$(otrinc_HEADERS)'; for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ f=$(am__strip_dir) \
+ echo " $(otrincHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(otrincdir)/$$f'"; \
+ $(otrincHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(otrincdir)/$$f"; \
+ done
+
+uninstall-otrincHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(otrinc_HEADERS)'; for p in $$list; do \
+ f=$(am__strip_dir) \
+ echo " rm -f '$(DESTDIR)$(otrincdir)/$$f'"; \
+ rm -f "$(DESTDIR)$(otrincdir)/$$f"; \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(otrincdir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-otrincHEADERS
+
+install-exec-am: install-libLTLIBRARIES
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am uninstall-libLTLIBRARIES \
+ uninstall-otrincHEADERS
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am \
+ install-libLTLIBRARIES install-man install-otrincHEADERS \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am \
+ uninstall-info-am uninstall-libLTLIBRARIES \
+ uninstall-otrincHEADERS
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libotr-3.2.0/src/auth.c b/libotr-3.2.0/src/auth.c
new file mode 100644
index 0000000..a6752c3
--- /dev/null
+++ b/libotr-3.2.0/src/auth.c
@@ -0,0 +1,1413 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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 <string.h>
+#include <assert.h>
+
+/* libotr headers */
+#include "b64.h"
+#include "privkey.h"
+#include "auth.h"
+#include "serial.h"
+
+/*
+ * Initialize the fields of an OtrlAuthInfo (already allocated).
+ */
+void otrl_auth_new(OtrlAuthInfo *auth)
+{
+ auth->authstate = OTRL_AUTHSTATE_NONE;
+ otrl_dh_keypair_init(&(auth->our_dh));
+ auth->our_keyid = 0;
+ auth->encgx = NULL;
+ auth->encgx_len = 0;
+ memset(auth->r, 0, 16);
+ memset(auth->hashgx, 0, 32);
+ auth->their_pub = NULL;
+ auth->their_keyid = 0;
+ auth->enc_c = NULL;
+ auth->enc_cp = NULL;
+ auth->mac_m1 = NULL;
+ auth->mac_m1p = NULL;
+ auth->mac_m2 = NULL;
+ auth->mac_m2p = NULL;
+ memset(auth->their_fingerprint, 0, 20);
+ auth->initiated = 0;
+ auth->protocol_version = 0;
+ memset(auth->secure_session_id, 0, 20);
+ auth->secure_session_id_len = 0;
+ auth->lastauthmsg = NULL;
+}
+
+/*
+ * Clear the fields of an OtrlAuthInfo (but leave it allocated).
+ */
+void otrl_auth_clear(OtrlAuthInfo *auth)
+{
+ auth->authstate = OTRL_AUTHSTATE_NONE;
+ otrl_dh_keypair_free(&(auth->our_dh));
+ auth->our_keyid = 0;
+ free(auth->encgx);
+ auth->encgx = NULL;
+ auth->encgx_len = 0;
+ memset(auth->r, 0, 16);
+ memset(auth->hashgx, 0, 32);
+ gcry_mpi_release(auth->their_pub);
+ auth->their_pub = NULL;
+ auth->their_keyid = 0;
+ gcry_cipher_close(auth->enc_c);
+ gcry_cipher_close(auth->enc_cp);
+ gcry_md_close(auth->mac_m1);
+ gcry_md_close(auth->mac_m1p);
+ gcry_md_close(auth->mac_m2);
+ gcry_md_close(auth->mac_m2p);
+ auth->enc_c = NULL;
+ auth->enc_cp = NULL;
+ auth->mac_m1 = NULL;
+ auth->mac_m1p = NULL;
+ auth->mac_m2 = NULL;
+ auth->mac_m2p = NULL;
+ memset(auth->their_fingerprint, 0, 20);
+ auth->initiated = 0;
+ auth->protocol_version = 0;
+ memset(auth->secure_session_id, 0, 20);
+ auth->secure_session_id_len = 0;
+ free(auth->lastauthmsg);
+ auth->lastauthmsg = NULL;
+}
+
+/*
+ * Start a fresh AKE (version 2) using the given OtrlAuthInfo. Generate
+ * a fresh DH keypair to use. If no error is returned, the message to
+ * transmit will be contained in auth->lastauthmsg.
+ */
+gcry_error_t otrl_auth_start_v2(OtrlAuthInfo *auth)
+{
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ const enum gcry_mpi_format format = GCRYMPI_FMT_USG;
+ size_t npub;
+ gcry_cipher_hd_t enc = NULL;
+ unsigned char ctr[16];
+ unsigned char *buf, *bufp;
+ size_t buflen, lenp;
+
+ /* Clear out this OtrlAuthInfo and start over */
+ otrl_auth_clear(auth);
+ auth->initiated = 1;
+
+ otrl_dh_gen_keypair(DH1536_GROUP_ID, &(auth->our_dh));
+ auth->our_keyid = 1;
+
+ /* Pick an encryption key */
+ gcry_randomize(auth->r, 16, GCRY_STRONG_RANDOM);
+
+ /* Allocate space for the encrypted g^x */
+ gcry_mpi_print(format, NULL, 0, &npub, auth->our_dh.pub);
+ auth->encgx = malloc(4+npub);
+ if (auth->encgx == NULL) goto memerr;
+ auth->encgx_len = 4+npub;
+ bufp = auth->encgx;
+ lenp = auth->encgx_len;
+ write_mpi(auth->our_dh.pub, npub, "g^x");
+ assert(lenp == 0);
+
+ /* Hash g^x */
+ gcry_md_hash_buffer(GCRY_MD_SHA256, auth->hashgx, auth->encgx,
+ auth->encgx_len);
+
+ /* Encrypt g^x using the key r */
+ err = gcry_cipher_open(&enc, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CTR,
+ GCRY_CIPHER_SECURE);
+ if (err) goto err;
+
+ err = gcry_cipher_setkey(enc, auth->r, 16);
+ if (err) goto err;
+
+ memset(ctr, 0, 16);
+ err = gcry_cipher_setctr(enc, ctr, 16);
+ if (err) goto err;
+
+ err = gcry_cipher_encrypt(enc, auth->encgx, auth->encgx_len, NULL, 0);
+ if (err) goto err;
+
+ gcry_cipher_close(enc);
+ enc = NULL;
+
+ /* Now serialize the message */
+ lenp = 3 + 4 + auth->encgx_len + 4 + 32;
+ bufp = malloc(lenp);
+ if (bufp == NULL) goto memerr;
+ buf = bufp;
+ buflen = lenp;
+
+ memmove(bufp, "\x00\x02\x02", 3); /* header */
+ debug_data("Header", bufp, 3);
+ bufp += 3; lenp -= 3;
+
+ /* Encrypted g^x */
+ write_int(auth->encgx_len);
+ debug_int("Enc gx len", bufp-4);
+ memmove(bufp, auth->encgx, auth->encgx_len);
+ debug_data("Enc gx", bufp, auth->encgx_len);
+ bufp += auth->encgx_len; lenp -= auth->encgx_len;
+
+ /* Hashed g^x */
+ write_int(32);
+ debug_int("hashgx len", bufp-4);
+ memmove(bufp, auth->hashgx, 32);
+ debug_data("hashgx", bufp, 32);
+ bufp += 32; lenp -= 32;
+
+ assert(lenp == 0);
+
+ auth->lastauthmsg = otrl_base64_otr_encode(buf, buflen);
+ free(buf);
+ if (auth->lastauthmsg == NULL) goto memerr;
+ auth->authstate = OTRL_AUTHSTATE_AWAITING_DHKEY;
+
+ return err;
+
+memerr:
+ err = gcry_error(GPG_ERR_ENOMEM);
+err:
+ otrl_auth_clear(auth);
+ gcry_cipher_close(enc);
+ return err;
+}
+
+/*
+ * Create a D-H Key Message using the our_dh value in the given auth,
+ * and store it in auth->lastauthmsg.
+ */
+static gcry_error_t create_key_message(OtrlAuthInfo *auth)
+{
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ const enum gcry_mpi_format format = GCRYMPI_FMT_USG;
+ unsigned char *buf, *bufp;
+ size_t buflen, lenp;
+ size_t npub;
+
+ gcry_mpi_print(format, NULL, 0, &npub, auth->our_dh.pub);
+ buflen = 3 + 4 + npub;
+ buf = malloc(buflen);
+ if (buf == NULL) goto memerr;
+ bufp = buf;
+ lenp = buflen;
+
+ memmove(bufp, "\x00\x02\x0a", 3); /* header */
+ debug_data("Header", bufp, 3);
+ bufp += 3; lenp -= 3;
+
+ /* g^y */
+ write_mpi(auth->our_dh.pub, npub, "g^y");
+
+ assert(lenp == 0);
+
+ free(auth->lastauthmsg);
+ auth->lastauthmsg = otrl_base64_otr_encode(buf, buflen);
+ free(buf);
+ if (auth->lastauthmsg == NULL) goto memerr;
+
+ return err;
+
+memerr:
+ err = gcry_error(GPG_ERR_ENOMEM);
+ return err;
+}
+
+/*
+ * Handle an incoming D-H Commit Message. If no error is returned, the
+ * message to send will be left in auth->lastauthmsg. Generate a fresh
+ * keypair to use.
+ */
+gcry_error_t otrl_auth_handle_commit(OtrlAuthInfo *auth,
+ const char *commitmsg)
+{
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ unsigned char *buf = NULL, *bufp = NULL, *encbuf = NULL;
+ unsigned char hashbuf[32];
+ size_t buflen, lenp, enclen, hashlen;
+ int res;
+
+ res = otrl_base64_otr_decode(commitmsg, &buf, &buflen);
+ if (res == -1) goto memerr;
+ if (res == -2) goto invval;
+
+ bufp = buf;
+ lenp = buflen;
+
+ /* Header */
+ require_len(3);
+ if (memcmp(bufp, "\x00\x02\x02", 3)) goto invval;
+ bufp += 3; lenp -= 3;
+
+ /* Encrypted g^x */
+ read_int(enclen);
+ require_len(enclen);
+ encbuf = malloc(enclen);
+ if (encbuf == NULL && enclen > 0) goto memerr;
+ memmove(encbuf, bufp, enclen);
+ bufp += enclen; lenp -= enclen;
+
+ /* Hashed g^x */
+ read_int(hashlen);
+ if (hashlen != 32) goto invval;
+ require_len(32);
+ memmove(hashbuf, bufp, 32);
+ bufp += 32; lenp -= 32;
+
+ if (lenp != 0) goto invval;
+ free(buf);
+ buf = NULL;
+
+ switch(auth->authstate) {
+ case OTRL_AUTHSTATE_NONE:
+ case OTRL_AUTHSTATE_AWAITING_SIG:
+ case OTRL_AUTHSTATE_V1_SETUP:
+
+ /* Store the incoming information */
+ otrl_auth_clear(auth);
+ otrl_dh_gen_keypair(DH1536_GROUP_ID, &(auth->our_dh));
+ auth->our_keyid = 1;
+ auth->encgx = encbuf;
+ encbuf = NULL;
+ auth->encgx_len = enclen;
+ memmove(auth->hashgx, hashbuf, 32);
+
+ /* Create a D-H Key Message */
+ err = create_key_message(auth);
+ if (err) goto err;
+ auth->authstate = OTRL_AUTHSTATE_AWAITING_REVEALSIG;
+
+ break;
+
+ case OTRL_AUTHSTATE_AWAITING_DHKEY:
+ /* We sent a D-H Commit Message, and we also received one
+ * back. Compare the hashgx values to see which one wins. */
+ if (memcmp(auth->hashgx, hashbuf, 32) > 0) {
+ /* Ours wins. Ignore the message we received, and just
+ * resend the same D-H Commit message again. */
+ free(encbuf);
+ encbuf = NULL;
+ } else {
+ /* Ours loses. Use the incoming parameters instead. */
+ otrl_auth_clear(auth);
+ otrl_dh_gen_keypair(DH1536_GROUP_ID, &(auth->our_dh));
+ auth->our_keyid = 1;
+ auth->encgx = encbuf;
+ encbuf = NULL;
+ auth->encgx_len = enclen;
+ memmove(auth->hashgx, hashbuf, 32);
+
+ /* Create a D-H Key Message */
+ err = create_key_message(auth);
+ if (err) goto err;
+ auth->authstate = OTRL_AUTHSTATE_AWAITING_REVEALSIG;
+ }
+ break;
+ case OTRL_AUTHSTATE_AWAITING_REVEALSIG:
+ /* Use the incoming parameters, but just retransmit the old
+ * D-H Key Message. */
+ free(auth->encgx);
+ auth->encgx = encbuf;
+ encbuf = NULL;
+ auth->encgx_len = enclen;
+ memmove(auth->hashgx, hashbuf, 32);
+ break;
+ }
+
+ return err;
+
+invval:
+ err = gcry_error(GPG_ERR_INV_VALUE);
+ goto err;
+memerr:
+ err = gcry_error(GPG_ERR_ENOMEM);
+err:
+ free(buf);
+ free(encbuf);
+ return err;
+}
+
+/*
+ * Calculate the encrypted part of the Reveal Signature and Signature
+ * Messages, given a MAC key, an encryption key, two DH public keys, an
+ * authentication public key (contained in an OtrlPrivKey structure),
+ * and a keyid. If no error is returned, *authbufp will point to the
+ * result, and *authlenp will point to its length.
+ */
+static gcry_error_t calculate_pubkey_auth(unsigned char **authbufp,
+ size_t *authlenp, gcry_md_hd_t mackey, gcry_cipher_hd_t enckey,
+ gcry_mpi_t our_dh_pub, gcry_mpi_t their_dh_pub,
+ OtrlPrivKey *privkey, unsigned int keyid)
+{
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ const enum gcry_mpi_format format = GCRYMPI_FMT_USG;
+ size_t ourpublen, theirpublen, totallen, lenp;
+ unsigned char *buf = NULL, *bufp = NULL;
+ unsigned char macbuf[32];
+ unsigned char *sigbuf = NULL;
+ size_t siglen;
+
+ /* How big are the DH public keys? */
+ gcry_mpi_print(format, NULL, 0, &ourpublen, our_dh_pub);
+ gcry_mpi_print(format, NULL, 0, &theirpublen, their_dh_pub);
+
+ /* How big is the total structure to be MAC'd? */
+ totallen = 4 + ourpublen + 4 + theirpublen + 2 + privkey->pubkey_datalen
+ + 4;
+ buf = malloc(totallen);
+ if (buf == NULL) goto memerr;
+
+ bufp = buf;
+ lenp = totallen;
+
+ /* Write the data to be MAC'd */
+ write_mpi(our_dh_pub, ourpublen, "Our DH pubkey");
+ write_mpi(their_dh_pub, theirpublen, "Their DH pubkey");
+ bufp[0] = ((privkey->pubkey_type) >> 16) & 0xff;
+ bufp[1] = (privkey->pubkey_type) & 0xff;
+ bufp += 2; lenp -= 2;
+ memmove(bufp, privkey->pubkey_data, privkey->pubkey_datalen);
+ debug_data("Pubkey", bufp, privkey->pubkey_datalen);
+ bufp += privkey->pubkey_datalen; lenp -= privkey->pubkey_datalen;
+ write_int(keyid);
+ debug_int("Keyid", bufp-4);
+
+ assert(lenp == 0);
+
+ /* Do the MAC */
+ gcry_md_reset(mackey);
+ gcry_md_write(mackey, buf, totallen);
+ memmove(macbuf, gcry_md_read(mackey, GCRY_MD_SHA256), 32);
+
+ free(buf);
+ buf = NULL;
+
+ /* Sign the MAC */
+ err = otrl_privkey_sign(&sigbuf, &siglen, privkey, macbuf, 32);
+ if (err) goto err;
+
+ /* Calculate the total size of the structure to be encrypted */
+ totallen = 2 + privkey->pubkey_datalen + 4 + siglen;
+ buf = malloc(totallen);
+ if (buf == NULL) goto memerr;
+ bufp = buf;
+ lenp = totallen;
+
+ /* Write the data to be encrypted */
+ bufp[0] = ((privkey->pubkey_type) >> 16) & 0xff;
+ bufp[1] = (privkey->pubkey_type) & 0xff;
+ bufp += 2; lenp -= 2;
+ memmove(bufp, privkey->pubkey_data, privkey->pubkey_datalen);
+ debug_data("Pubkey", bufp, privkey->pubkey_datalen);
+ bufp += privkey->pubkey_datalen; lenp -= privkey->pubkey_datalen;
+ write_int(keyid);
+ debug_int("Keyid", bufp-4);
+ memmove(bufp, sigbuf, siglen);
+ debug_data("Signature", bufp, siglen);
+ bufp += siglen; lenp -= siglen;
+ free(sigbuf);
+ sigbuf = NULL;
+
+ assert(lenp == 0);
+
+ /* Now do the encryption */
+ err = gcry_cipher_encrypt(enckey, buf, totallen, NULL, 0);
+ if (err) goto err;
+
+ *authbufp = buf;
+ buf = NULL;
+ *authlenp = totallen;
+
+ return err;
+memerr:
+ err = gcry_error(GPG_ERR_ENOMEM);
+err:
+ free(buf);
+ free(sigbuf);
+ return err;
+}
+
+/*
+ * Decrypt the authenticator in the Reveal Signature and Signature
+ * Messages, given a MAC key, and encryption key, and two DH public
+ * keys. The fingerprint of the received public key will get put into
+ * fingerprintbufp, and the received keyid will get put in *keyidp.
+ * The encrypted data pointed to by authbuf will be decrypted in place.
+ */
+static gcry_error_t check_pubkey_auth(unsigned char fingerprintbufp[20],
+ unsigned int *keyidp, unsigned char *authbuf, size_t authlen,
+ gcry_md_hd_t mackey, gcry_cipher_hd_t enckey,
+ gcry_mpi_t our_dh_pub, gcry_mpi_t their_dh_pub)
+{
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ const enum gcry_mpi_format format = GCRYMPI_FMT_USG;
+ size_t ourpublen, theirpublen, totallen, lenp;
+ unsigned char *buf = NULL, *bufp = NULL;
+ unsigned char macbuf[32];
+ unsigned short pubkey_type;
+ gcry_mpi_t p,q,g,y;
+ gcry_sexp_t pubs = NULL;
+ unsigned int received_keyid;
+ unsigned char *fingerprintstart, *fingerprintend, *sigbuf;
+ size_t siglen;
+
+ /* Start by decrypting it */
+ err = gcry_cipher_decrypt(enckey, authbuf, authlen, NULL, 0);
+ if (err) goto err;
+
+ bufp = authbuf;
+ lenp = authlen;
+
+ /* Get the public key and calculate its fingerprint */
+ require_len(2);
+ pubkey_type = (bufp[0] << 8) + bufp[1];
+ bufp += 2; lenp -= 2;
+ if (pubkey_type != OTRL_PUBKEY_TYPE_DSA) goto invval;
+ fingerprintstart = bufp;
+ read_mpi(p);
+ read_mpi(q);
+ read_mpi(g);
+ read_mpi(y);
+ fingerprintend = bufp;
+ gcry_md_hash_buffer(GCRY_MD_SHA1, fingerprintbufp,
+ fingerprintstart, fingerprintend-fingerprintstart);
+ gcry_sexp_build(&pubs, NULL,
+ "(public-key (dsa (p %m)(q %m)(g %m)(y %m)))", p, q, g, y);
+ gcry_mpi_release(p);
+ gcry_mpi_release(q);
+ gcry_mpi_release(g);
+ gcry_mpi_release(y);
+
+ /* Get the keyid */
+ read_int(received_keyid);
+ if (received_keyid == 0) goto invval;
+
+ /* Get the signature */
+ sigbuf = bufp;
+ siglen = lenp;
+
+ /* How big are the DH public keys? */
+ gcry_mpi_print(format, NULL, 0, &ourpublen, our_dh_pub);
+ gcry_mpi_print(format, NULL, 0, &theirpublen, their_dh_pub);
+
+ /* Now calculate the message to be MAC'd. */
+ totallen = 4 + ourpublen + 4 + theirpublen + 2 +
+ (fingerprintend - fingerprintstart) + 4;
+ buf = malloc(totallen);
+ if (buf == NULL) goto memerr;
+
+ bufp = buf;
+ lenp = totallen;
+
+ write_mpi(their_dh_pub, theirpublen, "Their DH pubkey");
+ write_mpi(our_dh_pub, ourpublen, "Our DH pubkey");
+ bufp[0] = (pubkey_type >> 16) & 0xff;
+ bufp[1] = pubkey_type & 0xff;
+ bufp += 2; lenp -= 2;
+ memmove(bufp, fingerprintstart, fingerprintend - fingerprintstart);
+ debug_data("Pubkey", bufp, fingerprintend - fingerprintstart);
+ bufp += fingerprintend - fingerprintstart;
+ lenp -= fingerprintend - fingerprintstart;
+ write_int(received_keyid);
+ debug_int("Keyid", bufp-4);
+
+ assert(lenp == 0);
+
+ /* Do the MAC */
+ gcry_md_reset(mackey);
+ gcry_md_write(mackey, buf, totallen);
+ memmove(macbuf, gcry_md_read(mackey, GCRY_MD_SHA256), 32);
+
+ free(buf);
+ buf = NULL;
+
+ /* Verify the signature on the MAC */
+ err = otrl_privkey_verify(sigbuf, siglen, pubkey_type, pubs, macbuf, 32);
+ if (err) goto err;
+ gcry_sexp_release(pubs);
+ pubs = NULL;
+
+ /* Everything checked out */
+ *keyidp = received_keyid;
+
+ return err;
+invval:
+ err = gcry_error(GPG_ERR_INV_VALUE);
+ goto err;
+memerr:
+ err = gcry_error(GPG_ERR_ENOMEM);
+err:
+ free(buf);
+ gcry_sexp_release(pubs);
+ return err;
+}
+
+/*
+ * Create a Reveal Signature Message using the values in the given auth,
+ * and store it in auth->lastauthmsg. Use the given privkey to sign the
+ * message.
+ */
+static gcry_error_t create_revealsig_message(OtrlAuthInfo *auth,
+ OtrlPrivKey *privkey)
+{
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ unsigned char *buf = NULL, *bufp, *startmac;
+ size_t buflen, lenp;
+
+ unsigned char *authbuf = NULL;
+ size_t authlen;
+
+ /* Get the encrypted authenticator */
+ err = calculate_pubkey_auth(&authbuf, &authlen, auth->mac_m1, auth->enc_c,
+ auth->our_dh.pub, auth->their_pub, privkey, auth->our_keyid);
+ if (err) goto err;
+
+ buflen = 3 + 4 + 16 + 4 + authlen + 20;
+ buf = malloc(buflen);
+ if (buf == NULL) goto memerr;
+
+ bufp = buf;
+ lenp = buflen;
+
+ memmove(bufp, "\x00\x02\x11", 3); /* header */
+ debug_data("Header", bufp, 3);
+ bufp += 3; lenp -= 3;
+
+ /* r */
+ write_int(16);
+ memmove(bufp, auth->r, 16);
+ debug_data("r", bufp, 16);
+ bufp += 16; lenp -= 16;
+
+ /* Encrypted authenticator */
+ startmac = bufp;
+ write_int(authlen);
+ memmove(bufp, authbuf, authlen);
+ debug_data("auth", bufp, authlen);
+ bufp += authlen; lenp -= authlen;
+ free(authbuf);
+ authbuf = NULL;
+
+ /* MAC it, but only take the first 20 bytes */
+ gcry_md_reset(auth->mac_m2);
+ gcry_md_write(auth->mac_m2, startmac, bufp - startmac);
+ memmove(bufp, gcry_md_read(auth->mac_m2, GCRY_MD_SHA256), 20);
+ debug_data("MAC", bufp, 20);
+ bufp += 20; lenp -= 20;
+
+ assert(lenp == 0);
+
+ free(auth->lastauthmsg);
+ auth->lastauthmsg = otrl_base64_otr_encode(buf, buflen);
+ if (auth->lastauthmsg == NULL) goto memerr;
+ free(buf);
+ buf = NULL;
+
+ return err;
+
+memerr:
+ err = gcry_error(GPG_ERR_ENOMEM);
+err:
+ free(buf);
+ free(authbuf);
+ return err;
+}
+
+/*
+ * Create a Signature Message using the values in the given auth, and
+ * store it in auth->lastauthmsg. Use the given privkey to sign the
+ * message.
+ */
+static gcry_error_t create_signature_message(OtrlAuthInfo *auth,
+ OtrlPrivKey *privkey)
+{
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ unsigned char *buf = NULL, *bufp, *startmac;
+ size_t buflen, lenp;
+
+ unsigned char *authbuf = NULL;
+ size_t authlen;
+
+ /* Get the encrypted authenticator */
+ err = calculate_pubkey_auth(&authbuf, &authlen, auth->mac_m1p,
+ auth->enc_cp, auth->our_dh.pub, auth->their_pub, privkey,
+ auth->our_keyid);
+ if (err) goto err;
+
+ buflen = 3 + 4 + authlen + 20;
+ buf = malloc(buflen);
+ if (buf == NULL) goto memerr;
+
+ bufp = buf;
+ lenp = buflen;
+
+ memmove(bufp, "\x00\x02\x12", 3); /* header */
+ debug_data("Header", bufp, 3);
+ bufp += 3; lenp -= 3;
+
+ /* Encrypted authenticator */
+ startmac = bufp;
+ write_int(authlen);
+ memmove(bufp, authbuf, authlen);
+ debug_data("auth", bufp, authlen);
+ bufp += authlen; lenp -= authlen;
+ free(authbuf);
+ authbuf = NULL;
+
+ /* MAC it, but only take the first 20 bytes */
+ gcry_md_reset(auth->mac_m2p);
+ gcry_md_write(auth->mac_m2p, startmac, bufp - startmac);
+ memmove(bufp, gcry_md_read(auth->mac_m2p, GCRY_MD_SHA256), 20);
+ debug_data("MAC", bufp, 20);
+ bufp += 20; lenp -= 20;
+
+ assert(lenp == 0);
+
+ free(auth->lastauthmsg);
+ auth->lastauthmsg = otrl_base64_otr_encode(buf, buflen);
+ if (auth->lastauthmsg == NULL) goto memerr;
+ free(buf);
+ buf = NULL;
+
+ return err;
+
+memerr:
+ err = gcry_error(GPG_ERR_ENOMEM);
+err:
+ free(buf);
+ free(authbuf);
+ return err;
+}
+
+/*
+ * Handle an incoming D-H Key Message. If no error is returned, and
+ * *havemsgp is 1, the message to sent will be left in auth->lastauthmsg.
+ * Use the given private authentication key to sign messages.
+ */
+gcry_error_t otrl_auth_handle_key(OtrlAuthInfo *auth, const char *keymsg,
+ int *havemsgp, OtrlPrivKey *privkey)
+{
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ unsigned char *buf = NULL, *bufp = NULL;
+ size_t buflen, lenp;
+ gcry_mpi_t incoming_pub = NULL;
+ int res;
+
+ *havemsgp = 0;
+
+ res = otrl_base64_otr_decode(keymsg, &buf, &buflen);
+ if (res == -1) goto memerr;
+ if (res == -2) goto invval;
+
+ bufp = buf;
+ lenp = buflen;
+
+ /* Header */
+ if (memcmp(bufp, "\x00\x02\x0a", 3)) goto invval;
+ bufp += 3; lenp -= 3;
+
+ /* g^y */
+ read_mpi(incoming_pub);
+
+ if (lenp != 0) goto invval;
+ free(buf);
+ buf = NULL;
+
+ switch(auth->authstate) {
+ case OTRL_AUTHSTATE_AWAITING_DHKEY:
+ /* Store the incoming public key */
+ gcry_mpi_release(auth->their_pub);
+ auth->their_pub = incoming_pub;
+ incoming_pub = NULL;
+
+ /* Compute the encryption and MAC keys */
+ err = otrl_dh_compute_v2_auth_keys(&(auth->our_dh),
+ auth->their_pub, auth->secure_session_id,
+ &(auth->secure_session_id_len),
+ &(auth->enc_c), &(auth->enc_cp),
+ &(auth->mac_m1), &(auth->mac_m1p),
+ &(auth->mac_m2), &(auth->mac_m2p));
+ if (err) goto err;
+
+ /* Create the Reveal Signature Message */
+ err = create_revealsig_message(auth, privkey);
+ if (err) goto err;
+ *havemsgp = 1;
+ auth->authstate = OTRL_AUTHSTATE_AWAITING_SIG;
+
+ break;
+
+ case OTRL_AUTHSTATE_AWAITING_SIG:
+ if (gcry_mpi_cmp(incoming_pub, auth->their_pub) == 0) {
+ /* Retransmit the Reveal Signature Message */
+ *havemsgp = 1;
+ } else {
+ /* Ignore this message */
+ *havemsgp = 0;
+ }
+ break;
+ case OTRL_AUTHSTATE_NONE:
+ case OTRL_AUTHSTATE_AWAITING_REVEALSIG:
+ case OTRL_AUTHSTATE_V1_SETUP:
+ /* Ignore this message */
+ *havemsgp = 0;
+ break;
+ }
+
+ gcry_mpi_release(incoming_pub);
+ return err;
+
+invval:
+ err = gcry_error(GPG_ERR_INV_VALUE);
+ goto err;
+memerr:
+ err = gcry_error(GPG_ERR_ENOMEM);
+err:
+ free(buf);
+ gcry_mpi_release(incoming_pub);
+ return err;
+}
+
+/*
+ * Handle an incoming Reveal Signature Message. If no error is
+ * returned, and *havemsgp is 1, the message to be sent will be left in
+ * auth->lastauthmsg. Use the given private authentication key to sign
+ * messages. Call the auth_succeeded callback if authentication is
+ * successful.
+ */
+gcry_error_t otrl_auth_handle_revealsig(OtrlAuthInfo *auth,
+ const char *revealmsg, int *havemsgp, OtrlPrivKey *privkey,
+ gcry_error_t (*auth_succeeded)(const OtrlAuthInfo *auth, void *asdata),
+ void *asdata)
+{
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ unsigned char *buf = NULL, *bufp = NULL, *gxbuf = NULL;
+ unsigned char *authstart, *authend, *macstart;
+ size_t buflen, lenp, rlen, authlen;
+ gcry_cipher_hd_t enc = NULL;
+ gcry_mpi_t incoming_pub = NULL;
+ unsigned char ctr[16], hashbuf[32];
+ int res;
+
+ *havemsgp = 0;
+
+ res = otrl_base64_otr_decode(revealmsg, &buf, &buflen);
+ if (res == -1) goto memerr;
+ if (res == -2) goto invval;
+
+ bufp = buf;
+ lenp = buflen;
+
+ /* Header */
+ if (memcmp(bufp, "\x00\x02\x11", 3)) goto invval;
+ bufp += 3; lenp -= 3;
+
+ /* r */
+ read_int(rlen);
+ if (rlen != 16) goto invval;
+ require_len(rlen);
+ memmove(auth->r, bufp, rlen);
+ bufp += rlen; lenp -= rlen;
+
+ /* auth */
+ authstart = bufp;
+ read_int(authlen);
+ require_len(authlen);
+ bufp += authlen; lenp -= authlen;
+ authend = bufp;
+
+ /* MAC */
+ require_len(20);
+ macstart = bufp;
+ bufp += 20; lenp -= 20;
+
+ if (lenp != 0) goto invval;
+
+ switch(auth->authstate) {
+ case OTRL_AUTHSTATE_AWAITING_REVEALSIG:
+ gxbuf = malloc(auth->encgx_len);
+ if (auth->encgx_len && gxbuf == NULL) goto memerr;
+
+ /* Use r to decrypt the value of g^x we received earlier */
+ err = gcry_cipher_open(&enc, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CTR,
+ GCRY_CIPHER_SECURE);
+ if (err) goto err;
+
+ err = gcry_cipher_setkey(enc, auth->r, 16);
+ if (err) goto err;
+
+ memset(ctr, 0, 16);
+ err = gcry_cipher_setctr(enc, ctr, 16);
+ if (err) goto err;
+
+ err = gcry_cipher_decrypt(enc, gxbuf, auth->encgx_len,
+ auth->encgx, auth->encgx_len);
+ if (err) goto err;
+
+ gcry_cipher_close(enc);
+ enc = NULL;
+
+ /* Check the hash */
+ gcry_md_hash_buffer(GCRY_MD_SHA256, hashbuf, gxbuf,
+ auth->encgx_len);
+ if (memcmp(hashbuf, auth->hashgx, 32)) goto decfail;
+
+ /* Extract g^x */
+ bufp = gxbuf;
+ lenp = auth->encgx_len;
+
+ read_mpi(incoming_pub);
+ free(gxbuf);
+ gxbuf = NULL;
+
+ if (lenp != 0) goto invval;
+
+ gcry_mpi_release(auth->their_pub);
+ auth->their_pub = incoming_pub;
+ incoming_pub = NULL;
+
+ /* Compute the encryption and MAC keys */
+ err = otrl_dh_compute_v2_auth_keys(&(auth->our_dh),
+ auth->their_pub, auth->secure_session_id,
+ &(auth->secure_session_id_len),
+ &(auth->enc_c), &(auth->enc_cp),
+ &(auth->mac_m1), &(auth->mac_m1p),
+ &(auth->mac_m2), &(auth->mac_m2p));
+ if (err) goto err;
+
+ /* Check the MAC */
+ gcry_md_reset(auth->mac_m2);
+ gcry_md_write(auth->mac_m2, authstart, authend - authstart);
+ if (memcmp(macstart,
+ gcry_md_read(auth->mac_m2, GCRY_MD_SHA256),
+ 20)) goto invval;
+
+ /* Check the auth */
+ err = check_pubkey_auth(auth->their_fingerprint,
+ &(auth->their_keyid), authstart + 4,
+ authend - authstart - 4, auth->mac_m1, auth->enc_c,
+ auth->our_dh.pub, auth->their_pub);
+ if (err) goto err;
+
+ authstart = NULL;
+ authend = NULL;
+ macstart = NULL;
+ free(buf);
+ buf = NULL;
+
+ /* Create the Signature Message */
+ err = create_signature_message(auth, privkey);
+ if (err) goto err;
+
+ /* No error? Then we've completed our end of the
+ * authentication. */
+ auth->protocol_version = 2;
+ auth->session_id_half = OTRL_SESSIONID_SECOND_HALF_BOLD;
+ if (auth_succeeded) err = auth_succeeded(auth, asdata);
+ *havemsgp = 1;
+ auth->our_keyid = 0;
+ auth->authstate = OTRL_AUTHSTATE_NONE;
+
+ break;
+ case OTRL_AUTHSTATE_NONE:
+ case OTRL_AUTHSTATE_AWAITING_DHKEY:
+ case OTRL_AUTHSTATE_AWAITING_SIG:
+ case OTRL_AUTHSTATE_V1_SETUP:
+ /* Ignore this message */
+ *havemsgp = 0;
+ free(buf);
+ buf = NULL;
+ break;
+ }
+
+ return err;
+
+decfail:
+ err = gcry_error(GPG_ERR_NO_ERROR);
+ goto err;
+invval:
+ err = gcry_error(GPG_ERR_INV_VALUE);
+ goto err;
+memerr:
+ err = gcry_error(GPG_ERR_ENOMEM);
+err:
+ free(buf);
+ free(gxbuf);
+ gcry_cipher_close(enc);
+ gcry_mpi_release(incoming_pub);
+ return err;
+}
+
+/*
+ * Handle an incoming Signature Message. If no error is returned, and
+ * *havemsgp is 1, the message to be sent will be left in
+ * auth->lastauthmsg. Call the auth_succeeded callback if
+ * authentication is successful.
+ */
+gcry_error_t otrl_auth_handle_signature(OtrlAuthInfo *auth,
+ const char *sigmsg, int *havemsgp,
+ gcry_error_t (*auth_succeeded)(const OtrlAuthInfo *auth, void *asdata),
+ void *asdata)
+{
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ unsigned char *buf = NULL, *bufp = NULL;
+ unsigned char *authstart, *authend, *macstart;
+ size_t buflen, lenp, authlen;
+ int res;
+
+ *havemsgp = 0;
+
+ res = otrl_base64_otr_decode(sigmsg, &buf, &buflen);
+ if (res == -1) goto memerr;
+ if (res == -2) goto invval;
+
+ bufp = buf;
+ lenp = buflen;
+
+ /* Header */
+ if (memcmp(bufp, "\x00\x02\x12", 3)) goto invval;
+ bufp += 3; lenp -= 3;
+
+ /* auth */
+ authstart = bufp;
+ read_int(authlen);
+ require_len(authlen);
+ bufp += authlen; lenp -= authlen;
+ authend = bufp;
+
+ /* MAC */
+ require_len(20);
+ macstart = bufp;
+ bufp += 20; lenp -= 20;
+
+ if (lenp != 0) goto invval;
+
+ switch(auth->authstate) {
+ case OTRL_AUTHSTATE_AWAITING_SIG:
+ /* Check the MAC */
+ gcry_md_reset(auth->mac_m2p);
+ gcry_md_write(auth->mac_m2p, authstart, authend - authstart);
+ if (memcmp(macstart,
+ gcry_md_read(auth->mac_m2p, GCRY_MD_SHA256),
+ 20)) goto invval;
+
+ /* Check the auth */
+ err = check_pubkey_auth(auth->their_fingerprint,
+ &(auth->their_keyid), authstart + 4,
+ authend - authstart - 4, auth->mac_m1p, auth->enc_cp,
+ auth->our_dh.pub, auth->their_pub);
+ if (err) goto err;
+
+ authstart = NULL;
+ authend = NULL;
+ macstart = NULL;
+ free(buf);
+ buf = NULL;
+
+ /* No error? Then we've completed our end of the
+ * authentication. */
+ auth->protocol_version = 2;
+ auth->session_id_half = OTRL_SESSIONID_FIRST_HALF_BOLD;
+ if (auth_succeeded) err = auth_succeeded(auth, asdata);
+ free(auth->lastauthmsg);
+ auth->lastauthmsg = NULL;
+ *havemsgp = 1;
+ auth->our_keyid = 0;
+ auth->authstate = OTRL_AUTHSTATE_NONE;
+
+ break;
+ case OTRL_AUTHSTATE_NONE:
+ case OTRL_AUTHSTATE_AWAITING_DHKEY:
+ case OTRL_AUTHSTATE_AWAITING_REVEALSIG:
+ case OTRL_AUTHSTATE_V1_SETUP:
+ /* Ignore this message */
+ *havemsgp = 0;
+ break;
+ }
+
+ return err;
+
+invval:
+ err = gcry_error(GPG_ERR_INV_VALUE);
+ goto err;
+memerr:
+ err = gcry_error(GPG_ERR_ENOMEM);
+err:
+ free(buf);
+ return err;
+}
+
+/* Version 1 routines, for compatibility */
+
+/*
+ * Create a verion 1 Key Exchange Message using the values in the given
+ * auth, and store it in auth->lastauthmsg. Set the Reply field to the
+ * given value, and use the given privkey to sign the message.
+ */
+static gcry_error_t create_v1_key_exchange_message(OtrlAuthInfo *auth,
+ unsigned char reply, OtrlPrivKey *privkey)
+{
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ const enum gcry_mpi_format format = GCRYMPI_FMT_USG;
+ unsigned char *buf = NULL, *bufp = NULL, *sigbuf = NULL;
+ size_t lenp, ourpublen, totallen, siglen;
+ unsigned char hashbuf[20];
+
+ if (privkey->pubkey_type != OTRL_PUBKEY_TYPE_DSA) {
+ return gpg_error(GPG_ERR_INV_VALUE);
+ }
+
+ /* How big is the DH public key? */
+ gcry_mpi_print(format, NULL, 0, &ourpublen, auth->our_dh.pub);
+
+ totallen = 3 + 1 + privkey->pubkey_datalen + 4 + 4 + ourpublen + 40;
+ buf = malloc(totallen);
+ if (buf == NULL) goto memerr;
+
+ bufp = buf;
+ lenp = totallen;
+
+ memmove(bufp, "\x00\x01\x0a", 3); /* header */
+ debug_data("Header", bufp, 3);
+ bufp += 3; lenp -= 3;
+
+ bufp[0] = reply;
+ debug_data("Reply", bufp, 1);
+ bufp += 1; lenp -= 1;
+
+ memmove(bufp, privkey->pubkey_data, privkey->pubkey_datalen);
+ debug_data("Pubkey", bufp, privkey->pubkey_datalen);
+ bufp += privkey->pubkey_datalen; lenp -= privkey->pubkey_datalen;
+
+ write_int(auth->our_keyid);
+ debug_int("Keyid", bufp-4);
+
+ write_mpi(auth->our_dh.pub, ourpublen, "D-H y");
+
+ /* Hash all the data written so far, and sign the hash */
+ gcry_md_hash_buffer(GCRY_MD_SHA1, hashbuf, buf, bufp - buf);
+
+ err = otrl_privkey_sign(&sigbuf, &siglen, privkey, hashbuf, 20);
+ if (err) goto err;
+
+ if (siglen != 40) goto invval;
+ memmove(bufp, sigbuf, 40);
+ debug_data("Signature", bufp, 40);
+ bufp += 40; lenp -= 40;
+ free(sigbuf);
+ sigbuf = NULL;
+
+ assert(lenp == 0);
+
+ free(auth->lastauthmsg);
+ auth->lastauthmsg = otrl_base64_otr_encode(buf, totallen);
+ if (auth->lastauthmsg == NULL) goto memerr;
+ free(buf);
+ buf = NULL;
+
+ return err;
+
+invval:
+ err = gcry_error(GPG_ERR_INV_VALUE);
+ goto err;
+memerr:
+ err = gcry_error(GPG_ERR_ENOMEM);
+err:
+ free(buf);
+ free(sigbuf);
+ return err;
+}
+
+/*
+ * Start a fresh AKE (version 1) using the given OtrlAuthInfo. If
+ * our_dh is NULL, generate a fresh DH keypair to use. Otherwise, use a
+ * copy of the one passed (with the given keyid). Use the given private
+ * key to sign the message. If no error is returned, the message to
+ * transmit will be contained in auth->lastauthmsg.
+ */
+gcry_error_t otrl_auth_start_v1(OtrlAuthInfo *auth, DH_keypair *our_dh,
+ unsigned int our_keyid, OtrlPrivKey *privkey)
+{
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+
+ /* Clear out this OtrlAuthInfo and start over */
+ otrl_auth_clear(auth);
+ auth->initiated = 1;
+
+ /* Import the given DH keypair, or else create a fresh one */
+ if (our_dh) {
+ otrl_dh_keypair_copy(&(auth->our_dh), our_dh);
+ auth->our_keyid = our_keyid;
+ } else {
+ otrl_dh_gen_keypair(DH1536_GROUP_ID, &(auth->our_dh));
+ auth->our_keyid = 1;
+ }
+
+ err = create_v1_key_exchange_message(auth, 0, privkey);
+ if (!err) {
+ auth->authstate = OTRL_AUTHSTATE_V1_SETUP;
+ }
+
+ return err;
+}
+
+/*
+ * Handle an incoming v1 Key Exchange Message. If no error is returned,
+ * and *havemsgp is 1, the message to be sent will be left in
+ * auth->lastauthmsg. Use the given private authentication key to sign
+ * messages. Call the auth_secceeded callback if authentication is
+ * successful. If non-NULL, use a copy of the given D-H keypair, with
+ * the given keyid.
+ */
+gcry_error_t otrl_auth_handle_v1_key_exchange(OtrlAuthInfo *auth,
+ const char *keyexchmsg, int *havemsgp, OtrlPrivKey *privkey,
+ DH_keypair *our_dh, unsigned int our_keyid,
+ gcry_error_t (*auth_succeeded)(const OtrlAuthInfo *auth, void *asdata),
+ void *asdata)
+{
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ unsigned char *buf = NULL, *bufp = NULL;
+ unsigned char *fingerprintstart, *fingerprintend;
+ unsigned char fingerprintbuf[20], hashbuf[20];
+ gcry_mpi_t p, q, g, y, received_pub = NULL;
+ gcry_sexp_t pubs = NULL;
+ size_t buflen, lenp;
+ unsigned char received_reply;
+ unsigned int received_keyid;
+ int res;
+
+ *havemsgp = 0;
+
+ res = otrl_base64_otr_decode(keyexchmsg, &buf, &buflen);
+ if (res == -1) goto memerr;
+ if (res == -2) goto invval;
+
+ bufp = buf;
+ lenp = buflen;
+
+ /* Header */
+ require_len(3);
+ if (memcmp(bufp, "\x00\x01\x0a", 3)) goto invval;
+ bufp += 3; lenp -= 3;
+
+ /* Reply */
+ require_len(1);
+ received_reply = bufp[0];
+ bufp += 1; lenp -= 1;
+
+ /* Public Key */
+ fingerprintstart = bufp;
+ read_mpi(p);
+ read_mpi(q);
+ read_mpi(g);
+ read_mpi(y);
+ fingerprintend = bufp;
+ gcry_md_hash_buffer(GCRY_MD_SHA1, fingerprintbuf,
+ fingerprintstart, fingerprintend-fingerprintstart);
+ gcry_sexp_build(&pubs, NULL,
+ "(public-key (dsa (p %m)(q %m)(g %m)(y %m)))", p, q, g, y);
+ gcry_mpi_release(p);
+ gcry_mpi_release(q);
+ gcry_mpi_release(g);
+ gcry_mpi_release(y);
+
+ /* keyid */
+ read_int(received_keyid);
+ if (received_keyid == 0) goto invval;
+
+ /* D-H pubkey */
+ read_mpi(received_pub);
+
+ /* Verify the signature */
+ if (lenp != 40) goto invval;
+ gcry_md_hash_buffer(GCRY_MD_SHA1, hashbuf, buf, bufp - buf);
+ err = otrl_privkey_verify(bufp, lenp, OTRL_PUBKEY_TYPE_DSA,
+ pubs, hashbuf, 20);
+ if (err) goto err;
+ gcry_sexp_release(pubs);
+ pubs = NULL;
+ free(buf);
+ buf = NULL;
+
+ if (auth->authstate != OTRL_AUTHSTATE_V1_SETUP && received_reply == 0x01) {
+ /* They're replying to something we never sent. We must be
+ * logged in more than once; ignore the message. */
+ err = gpg_error(GPG_ERR_NO_ERROR);
+ goto err;
+ }
+
+ if (auth->authstate != OTRL_AUTHSTATE_V1_SETUP) {
+ /* Clear the auth and start over */
+ otrl_auth_clear(auth);
+ }
+
+ /* Everything checked out */
+ auth->their_keyid = received_keyid;
+ gcry_mpi_release(auth->their_pub);
+ auth->their_pub = received_pub;
+ received_pub = NULL;
+ memmove(auth->their_fingerprint, fingerprintbuf, 20);
+
+ if (received_reply == 0x01) {
+ /* Don't send a reply to this. */
+ *havemsgp = 0;
+ } else {
+ /* Import the given DH keypair, or else create a fresh one */
+ if (our_dh) {
+ otrl_dh_keypair_copy(&(auth->our_dh), our_dh);
+ auth->our_keyid = our_keyid;
+ } else if (auth->our_keyid == 0) {
+ otrl_dh_gen_keypair(DH1536_GROUP_ID, &(auth->our_dh));
+ auth->our_keyid = 1;
+ }
+
+ /* Reply with our own Key Exchange Message */
+ err = create_v1_key_exchange_message(auth, 1, privkey);
+ if (err) goto err;
+ *havemsgp = 1;
+ }
+
+ /* Compute the session id */
+ err = otrl_dh_compute_v1_session_id(&(auth->our_dh),
+ auth->their_pub, auth->secure_session_id,
+ &(auth->secure_session_id_len),
+ &(auth->session_id_half));
+ if (err) goto err;
+
+ /* We've completed our end of the authentication */
+ auth->protocol_version = 1;
+ if (auth_succeeded) err = auth_succeeded(auth, asdata);
+ auth->our_keyid = 0;
+ auth->authstate = OTRL_AUTHSTATE_NONE;
+
+ return err;
+
+invval:
+ err = gcry_error(GPG_ERR_INV_VALUE);
+ goto err;
+memerr:
+ err = gcry_error(GPG_ERR_ENOMEM);
+err:
+ free(buf);
+ gcry_sexp_release(pubs);
+ gcry_mpi_release(received_pub);
+ return err;
+}
+
+#ifdef OTRL_TESTING_AUTH
+#include "mem.h"
+#include "privkey.h"
+
+#define CHECK_ERR if (err) { printf("Error: %s\n", gcry_strerror(err)); return 1; }
+
+static gcry_error_t starting(const OtrlAuthInfo *auth, void *asdata)
+{
+ char *name = asdata;
+
+ fprintf(stderr, "\nStarting ENCRYPTED mode for %s (v%d).\n", name, auth->protocol_version);
+
+ fprintf(stderr, "\nour_dh (%d):", auth->our_keyid);
+ gcry_mpi_dump(auth->our_dh.pub);
+ fprintf(stderr, "\ntheir_pub (%d):", auth->their_keyid);
+ gcry_mpi_dump(auth->their_pub);
+
+ debug_data("\nTheir fingerprint", auth->their_fingerprint, 20);
+ debug_data("\nSecure session id", auth->secure_session_id,
+ auth->secure_session_id_len);
+ fprintf(stderr, "Sessionid half: %d\n\n", auth->session_id_half);
+
+ return gpg_error(GPG_ERR_NO_ERROR);
+}
+
+int main(int argc, char **argv)
+{
+ OtrlAuthInfo alice, bob;
+ gcry_error_t err;
+ int havemsg;
+ OtrlUserState us;
+ OtrlPrivKey *alicepriv, *bobpriv;
+
+ otrl_mem_init();
+ otrl_dh_init();
+ otrl_auth_new(&alice);
+ otrl_auth_new(&bob);
+
+ us = otrl_userstate_create();
+ otrl_privkey_read(us, "/home/iang/.gaim/otr.private_key");
+ alicepriv = otrl_privkey_find(us, "oneeyedian", "prpl-oscar");
+ bobpriv = otrl_privkey_find(us, "otr4ian", "prpl-oscar");
+
+ printf("\n\n ***** V2 *****\n\n");
+
+ err = otrl_auth_start_v2(&bob, NULL, 0);
+ CHECK_ERR
+ printf("\nBob: %d\n%s\n\n", strlen(bob.lastauthmsg), bob.lastauthmsg);
+ err = otrl_auth_handle_commit(&alice, bob.lastauthmsg, NULL, 0);
+ CHECK_ERR
+ printf("\nAlice: %d\n%s\n\n", strlen(alice.lastauthmsg), alice.lastauthmsg);
+ err = otrl_auth_handle_key(&bob, alice.lastauthmsg, &havemsg, bobpriv);
+ CHECK_ERR
+ if (havemsg) {
+ printf("\nBob: %d\n%s\n\n", strlen(bob.lastauthmsg), bob.lastauthmsg);
+ } else {
+ printf("\nIGNORE\n\n");
+ }
+ err = otrl_auth_handle_revealsig(&alice, bob.lastauthmsg, &havemsg,
+ alicepriv, starting, "Alice");
+ CHECK_ERR
+ if (havemsg) {
+ printf("\nAlice: %d\n%s\n\n", strlen(alice.lastauthmsg), alice.lastauthmsg);
+ } else {
+ printf("\nIGNORE\n\n");
+ }
+ err = otrl_auth_handle_signature(&bob, alice.lastauthmsg, &havemsg,
+ starting, "Bob");
+ CHECK_ERR
+
+ printf("\n\n ***** V1 *****\n\n");
+
+ err = otrl_auth_start_v1(&bob, NULL, 0, bobpriv);
+ CHECK_ERR
+ printf("\nBob: %d\n%s\n\n", strlen(bob.lastauthmsg), bob.lastauthmsg);
+ err = otrl_auth_handle_v1_key_exchange(&alice, bob.lastauthmsg,
+ &havemsg, alicepriv, NULL, 0, starting, "Alice");
+ CHECK_ERR
+ if (havemsg) {
+ printf("\nAlice: %d\n%s\n\n", strlen(alice.lastauthmsg), alice.lastauthmsg);
+ } else {
+ printf("\nIGNORE\n\n");
+ }
+ err = otrl_auth_handle_v1_key_exchange(&bob, alice.lastauthmsg,
+ &havemsg, bobpriv, NULL, 0, starting, "Bob");
+ CHECK_ERR
+ if (havemsg) {
+ printf("\nBob: %d\n%s\n\n", strlen(bob.lastauthmsg), bob.lastauthmsg);
+ } else {
+ printf("\nIGNORE\n\n");
+ }
+
+ otrl_userstate_free(us);
+ otrl_auth_clear(&alice);
+ otrl_auth_clear(&bob);
+ return 0;
+}
+#endif
diff --git a/libotr-3.2.0/src/auth.h b/libotr-3.2.0/src/auth.h
new file mode 100644
index 0000000..6de75dd
--- /dev/null
+++ b/libotr-3.2.0/src/auth.h
@@ -0,0 +1,157 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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 __AUTH_H__
+#define __AUTH_H__
+
+#include <gcrypt.h>
+#include "dh.h"
+
+typedef enum {
+ OTRL_AUTHSTATE_NONE,
+ OTRL_AUTHSTATE_AWAITING_DHKEY,
+ OTRL_AUTHSTATE_AWAITING_REVEALSIG,
+ OTRL_AUTHSTATE_AWAITING_SIG,
+ OTRL_AUTHSTATE_V1_SETUP
+} OtrlAuthState;
+
+typedef struct {
+ OtrlAuthState authstate; /* Our state */
+
+ DH_keypair our_dh; /* Our D-H key */
+ unsigned int our_keyid; /* ...and its keyid */
+
+ unsigned char *encgx; /* The encrypted value of g^x */
+ size_t encgx_len; /* ...and its length */
+ unsigned char r[16]; /* The encryption key */
+
+ unsigned char hashgx[32]; /* SHA256(g^x) */
+
+ gcry_mpi_t their_pub; /* Their D-H public key */
+ unsigned int their_keyid; /* ...and its keyid */
+
+ gcry_cipher_hd_t enc_c, enc_cp; /* c and c' encryption keys */
+ gcry_md_hd_t mac_m1, mac_m1p; /* m1 and m1' MAC keys */
+ gcry_md_hd_t mac_m2, mac_m2p; /* m2 and m2' MAC keys */
+
+ unsigned char their_fingerprint[20]; /* The fingerprint of their
+ long-term signing key */
+
+ int initiated; /* Did we initiate this
+ authentication? */
+
+ unsigned int protocol_version; /* The protocol version number
+ used to authenticate. */
+
+ unsigned char secure_session_id[20]; /* The secure session id */
+ size_t secure_session_id_len; /* And its actual length,
+ which may be either 20 (for
+ v1) or 8 (for v2) */
+ OtrlSessionIdHalf session_id_half; /* Which half of the session
+ id gets shown in bold */
+
+ char *lastauthmsg; /* The last auth message
+ (base-64 encoded) we sent,
+ in case we need to
+ retransmit it. */
+} OtrlAuthInfo;
+
+#include "privkey-t.h"
+
+/*
+ * Initialize the fields of an OtrlAuthInfo (already allocated).
+ */
+void otrl_auth_new(OtrlAuthInfo *auth);
+
+/*
+ * Clear the fields of an OtrlAuthInfo (but leave it allocated).
+ */
+void otrl_auth_clear(OtrlAuthInfo *auth);
+
+/*
+ * Start a fresh AKE (version 2) using the given OtrlAuthInfo. Generate
+ * a fresh DH keypair to use. If no error is returned, the message to
+ * transmit will be contained in auth->lastauthmsg.
+ */
+gcry_error_t otrl_auth_start_v2(OtrlAuthInfo *auth);
+
+/*
+ * Handle an incoming D-H Commit Message. If no error is returned, the
+ * message to send will be left in auth->lastauthmsg. Generate a fresh
+ * keypair to use.
+ */
+gcry_error_t otrl_auth_handle_commit(OtrlAuthInfo *auth,
+ const char *commitmsg);
+
+/*
+ * Handle an incoming D-H Key Message. If no error is returned, and
+ * *havemsgp is 1, the message to sent will be left in auth->lastauthmsg.
+ * Use the given private authentication key to sign messages.
+ */
+gcry_error_t otrl_auth_handle_key(OtrlAuthInfo *auth, const char *keymsg,
+ int *havemsgp, OtrlPrivKey *privkey);
+
+/*
+ * Handle an incoming Reveal Signature Message. If no error is
+ * returned, and *havemsgp is 1, the message to be sent will be left in
+ * auth->lastauthmsg. Use the given private authentication key to sign
+ * messages. Call the auth_succeeded callback if authentication is
+ * successful.
+ */
+gcry_error_t otrl_auth_handle_revealsig(OtrlAuthInfo *auth,
+ const char *revealmsg, int *havemsgp, OtrlPrivKey *privkey,
+ gcry_error_t (*auth_succeeded)(const OtrlAuthInfo *auth, void *asdata),
+ void *asdata);
+
+/*
+ * Handle an incoming Signature Message. If no error is returned, and
+ * *havemsgp is 1, the message to be sent will be left in
+ * auth->lastauthmsg. Call the auth_succeeded callback if
+ * authentication is successful.
+ */
+gcry_error_t otrl_auth_handle_signature(OtrlAuthInfo *auth,
+ const char *sigmsg, int *havemsgp,
+ gcry_error_t (*auth_succeeded)(const OtrlAuthInfo *auth, void *asdata),
+ void *asdata);
+
+/*
+ * Start a fresh AKE (version 1) using the given OtrlAuthInfo. If
+ * our_dh is NULL, generate a fresh DH keypair to use. Otherwise, use a
+ * copy of the one passed (with the given keyid). Use the given private
+ * key to sign the message. If no error is returned, the message to
+ * transmit will be contained in auth->lastauthmsg.
+ */
+gcry_error_t otrl_auth_start_v1(OtrlAuthInfo *auth, DH_keypair *our_dh,
+ unsigned int our_keyid, OtrlPrivKey *privkey);
+
+/*
+ * Handle an incoming v1 Key Exchange Message. If no error is returned,
+ * and *havemsgp is 1, the message to be sent will be left in
+ * auth->lastauthmsg. Use the given private authentication key to sign
+ * messages. Call the auth_secceeded callback if authentication is
+ * successful. If non-NULL, use a copy of the given D-H keypair, with
+ * the given keyid.
+ */
+gcry_error_t otrl_auth_handle_v1_key_exchange(OtrlAuthInfo *auth,
+ const char *keyexchmsg, int *havemsgp, OtrlPrivKey *privkey,
+ DH_keypair *our_dh, unsigned int our_keyid,
+ gcry_error_t (*auth_succeeded)(const OtrlAuthInfo *auth, void *asdata),
+ void *asdata);
+
+#endif
diff --git a/libotr-3.2.0/src/b64.c b/libotr-3.2.0/src/b64.c
new file mode 100644
index 0000000..b8736da
--- /dev/null
+++ b/libotr-3.2.0/src/b64.c
@@ -0,0 +1,249 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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>
+#include <string.h>
+
+/* libotr headers */
+#include "b64.h"
+
+/*
+** Translation Table as described in RFC1113
+*/
+static const 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 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(unsigned char *data, const char *base64data,
+ size_t base64len)
+{
+ size_t datalen = 0;
+ 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;
+}
+
+/*
+ * Base64-encode a block of data, stick "?OTR:" and "." around it, and
+ * return the result, or NULL in the event of a memory error. The
+ * caller must free() the return value.
+ */
+char *otrl_base64_otr_encode(const unsigned char *buf, size_t buflen)
+{
+ char *base64buf;
+ size_t base64len;
+
+ /* Make the base64-encoding. */
+ base64len = ((buflen + 2) / 3) * 4;
+ base64buf = malloc(5 + base64len + 1 + 1);
+ if (base64buf == NULL) {
+ return NULL;
+ }
+ memmove(base64buf, "?OTR:", 5);
+ otrl_base64_encode(base64buf+5, buf, buflen);
+ base64buf[5 + base64len] = '.';
+ base64buf[5 + base64len + 1] = '\0';
+
+ return base64buf;
+}
+
+/*
+ * Base64-decode the portion of the given message between "?OTR:" and
+ * ".". Set *bufp to the decoded data, and set *lenp to its length.
+ * The caller must free() the result. Return 0 on success, -1 on a
+ * memory error, or -2 on invalid input.
+ */
+int otrl_base64_otr_decode(const char *msg, unsigned char **bufp,
+ size_t *lenp)
+{
+ char *otrtag, *endtag;
+ size_t msglen, rawlen;
+ unsigned char *rawmsg;
+
+ otrtag = strstr(msg, "?OTR:");
+ if (!otrtag) {
+ return -2;
+ }
+ endtag = strchr(otrtag, '.');
+ if (endtag) {
+ msglen = endtag-otrtag;
+ } else {
+ return -2;
+ }
+
+ /* Base64-decode the message */
+ rawlen = ((msglen-5) / 4) * 3; /* maximum possible */
+ rawmsg = malloc(rawlen);
+ if (!rawmsg && rawlen > 0) {
+ return -1;
+ }
+ rawlen = otrl_base64_decode(rawmsg, otrtag+5, msglen-5); /* actual size */
+
+ *bufp = rawmsg;
+ *lenp = rawlen;
+
+ return 0;
+}
diff --git a/libotr-3.2.0/src/b64.h b/libotr-3.2.0/src/b64.h
new file mode 100644
index 0000000..34ef03f
--- /dev/null
+++ b/libotr-3.2.0/src/b64.h
@@ -0,0 +1,57 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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(unsigned char *data, const char *base64data,
+ size_t base64len);
+
+/*
+ * Base64-encode a block of data, stick "?OTR:" and "." around it, and
+ * return the result, or NULL in the event of a memory error.
+ */
+char *otrl_base64_otr_encode(const unsigned char *buf, size_t buflen);
+
+/*
+ * Base64-decode the portion of the given message between "?OTR:" and
+ * ".". Set *bufp to the decoded data, and set *lenp to its length.
+ * The caller must free() the result. Return 0 on success, -1 on a
+ * memory error, or -2 on invalid input.
+ */
+int otrl_base64_otr_decode(const char *msg, unsigned char **bufp,
+ size_t *lenp);
+
+#endif
diff --git a/libotr-3.2.0/src/context.c b/libotr-3.2.0/src/context.c
new file mode 100644
index 0000000..e1601ec
--- /dev/null
+++ b/libotr-3.2.0/src/context.c
@@ -0,0 +1,330 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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"
+
+/* Create a new connection context. */
+static ConnContext * new_context(const char * user, const char * accountname,
+ const char * protocol)
+{
+ ConnContext * context;
+ OtrlSMState *smstate;
+ context = malloc(sizeof(*context));
+ assert(context != NULL);
+ context->username = strdup(user);
+ context->accountname = strdup(accountname);
+ context->protocol = strdup(protocol);
+ context->fragment = NULL;
+ context->fragment_len = 0;
+ context->fragment_n = 0;
+ context->fragment_k = 0;
+ context->msgstate = OTRL_MSGSTATE_PLAINTEXT;
+ otrl_auth_new(&(context->auth));
+
+ smstate = malloc(sizeof(OtrlSMState));
+ assert(smstate != NULL);
+ otrl_sm_state_new(smstate);
+ context->smstate = smstate;
+
+ context->fingerprint_root.fingerprint = NULL;
+ context->fingerprint_root.context = context;
+ context->fingerprint_root.next = NULL;
+ 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]));
+ memset(context->sessionid, 0, 20);
+ context->sessionid_len = 0;
+ context->protocol_version = 0;
+ context->numsavedkeys = 0;
+ context->preshared_secret = NULL;
+ context->preshared_secret_len = 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;
+}
+
+ConnContext * otrl_context_new(const char * user, const char * accountname,
+ const char * protocol)
+{
+ return new_context(user, accountname, protocol);
+}
+
+/* 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->trust = NULL;
+ 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;
+}
+
+/* Set the trust level for a given fingerprint */
+void otrl_context_set_trust(Fingerprint *fprint, const char *trust)
+{
+ if (fprint == NULL) return;
+
+ free(fprint->trust);
+ fprint->trust = trust ? strdup(trust) : NULL;
+}
+
+/* Set the preshared secret for a given fingerprint. Note that this
+ * currently only stores the secret in the ConnContext structure, but
+ * doesn't yet do anything with it. */
+void otrl_context_set_preshared_secret(ConnContext *context,
+ const unsigned char *secret, size_t secret_len)
+{
+ free(context->preshared_secret);
+ context->preshared_secret = NULL;
+ context->preshared_secret_len = 0;
+
+ if (secret_len) {
+ context->preshared_secret = malloc(secret_len);
+ if (context->preshared_secret) {
+ memmove(context->preshared_secret, secret, secret_len);
+ context->preshared_secret_len = secret_len;
+ }
+ }
+}
+
+/* Force a context into the OTRL_MSGSTATE_FINISHED state. */
+void otrl_context_force_finished(ConnContext *context)
+{
+ context->msgstate = OTRL_MSGSTATE_FINISHED;
+ otrl_auth_clear(&(context->auth));
+ free(context->fragment);
+ context->fragment = NULL;
+ context->fragment_len = 0;
+ context->fragment_n = 0;
+ context->fragment_k = 0;
+ 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;
+ context->our_keyid = 0;
+ otrl_dh_keypair_free(&(context->our_dh_key));
+ otrl_dh_keypair_free(&(context->our_old_dh_key));
+ 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]));
+ memset(context->sessionid, 0, 20);
+ context->sessionid_len = 0;
+ free(context->preshared_secret);
+ context->preshared_secret = NULL;
+ context->preshared_secret_len = 0;
+ context->protocol_version = 0;
+ context->numsavedkeys = 0;
+ free(context->saved_mac_keys);
+ context->saved_mac_keys = NULL;
+ gcry_free(context->lastmessage);
+ context->lastmessage = NULL;
+ context->may_retransmit = 0;
+ otrl_sm_state_free(context->smstate);
+}
+
+/* Force a context into the OTRL_MSGSTATE_PLAINTEXT state. */
+void otrl_context_force_plaintext(ConnContext *context)
+{
+ /* First clean up everything we'd need to do for the FINISHED state */
+ otrl_context_force_finished(context);
+
+ /* And just set the state properly */
+ context->msgstate = OTRL_MSGSTATE_PLAINTEXT;
+}
+
+/* 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 PLAINTEXT). Also, if it's not
+ * the fingerprint_root, but it's the only fingerprint, and we're
+ * PLAINTEXT, 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->msgstate == OTRL_MSGSTATE_PLAINTEXT &&
+ and_maybe_context) {
+ otrl_context_forget(context);
+ }
+ } else {
+ if (context->msgstate != OTRL_MSGSTATE_PLAINTEXT ||
+ context->active_fingerprint != fprint) {
+ free(fprint->fingerprint);
+ free(fprint->trust);
+ *(fprint->tous) = fprint->next;
+ if (fprint->next) {
+ fprint->next->tous = fprint->tous;
+ }
+ free(fprint);
+ if (context->msgstate == OTRL_MSGSTATE_PLAINTEXT &&
+ 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 PLAINTEXT. */
+void otrl_context_forget(ConnContext *context)
+{
+ if (context->msgstate != OTRL_MSGSTATE_PLAINTEXT) return;
+
+ /* Just to be safe, force to plaintext. This also frees any
+ * extraneous data lying around. */
+ otrl_context_force_plaintext(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);
+ free(context->smstate);
+ context->username = NULL;
+ context->accountname = NULL;
+ context->protocol = NULL;
+ context->smstate = NULL;
+
+ /* Free the application data, if it exists */
+ if (context->app_data && context->app_data_free) {
+ (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. */
+void otrl_context_forget_all(OtrlUserState us)
+{
+ while (us->context_root) {
+ otrl_context_force_plaintext(us->context_root);
+ otrl_context_forget(us->context_root);
+ }
+}
diff --git a/libotr-3.2.0/src/context.h b/libotr-3.2.0/src/context.h
new file mode 100644
index 0000000..91a1b45
--- /dev/null
+++ b/libotr-3.2.0/src/context.h
@@ -0,0 +1,182 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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"
+#include "auth.h"
+#include "sm.h"
+
+typedef enum {
+ OTRL_MSGSTATE_PLAINTEXT, /* Not yet started an encrypted
+ conversation */
+ OTRL_MSGSTATE_ENCRYPTED, /* Currently in an encrypted
+ conversation */
+ OTRL_MSGSTATE_FINISHED /* The remote side has sent us a
+ notification that he has ended
+ his end of the encrypted
+ conversation; prevent any
+ further messages from being
+ sent to him. */
+} OtrlMessageState;
+
+typedef struct s_fingerprint {
+ struct s_fingerprint *next; /* The next fingerprint in the list */
+ struct s_fingerprint **tous; /* A pointer to the pointer to us */
+ unsigned char *fingerprint; /* The fingerprint, or NULL */
+ struct context *context; /* The context to which we belong */
+ char *trust; /* The trust level of the fingerprint */
+} Fingerprint;
+
+typedef struct context {
+ struct context * next; /* Linked list pointer */
+ struct context ** tous; /* A pointer to the pointer to us */
+
+ char * username; /* The user this context is for */
+ char * accountname; /* The username is relative to
+ this account... */
+ char * protocol; /* ... and this protocol */
+
+ char *fragment; /* The part of the fragmented message
+ we've seen so far */
+ size_t fragment_len; /* The length of fragment */
+ unsigned short fragment_n; /* The total number of fragments
+ in this message */
+ unsigned short fragment_k; /* The highest fragment number
+ we've seen so far for this
+ message */
+
+ OtrlMessageState msgstate; /* The state of message disposition
+ with this user */
+ OtrlAuthInfo auth; /* The state of ongoing
+ authentication with 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] */
+
+ unsigned char sessionid[20]; /* The sessionid and bold half */
+ size_t sessionid_len; /* determined when this private */
+ OtrlSessionIdHalf sessionid_half; /* connection was established. */
+
+ unsigned int protocol_version; /* The version of OTR in use */
+
+ unsigned char *preshared_secret; /* A secret you share with this
+ user, in order to do
+ authentication. */
+ size_t preshared_secret_len; /* The length of the above secret. */
+
+ /* 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 *);
+
+ OtrlSMState *smstate; /* The state of the current
+ socialist millionaires exchange */
+} ConnContext;
+
+#include "userstate.h"
+
+ConnContext * otrl_context_new(const char * user, const char * accountname,
+ const char * protocol);
+
+/* 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);
+
+/* Set the trust level for a given fingerprint */
+void otrl_context_set_trust(Fingerprint *fprint, const char *trust);
+
+/* Set the preshared secret for a given fingerprint. Note that this
+ * currently only stores the secret in the ConnContext structure, but
+ * doesn't yet do anything with it. */
+void otrl_context_set_preshared_secret(ConnContext *context,
+ const unsigned char *secret, size_t secret_len);
+
+/* Force a context into the OTRL_MSGSTATE_FINISHED state. */
+void otrl_context_force_finished(ConnContext *context);
+
+/* Force a context into the OTRL_MSGSTATE_PLAINTEXT state. */
+void otrl_context_force_plaintext(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 PLAINTEXT). Also, if it's not
+ * the fingerprint_root, but it's the only fingerprint, and we're
+ * PLAINTEXT, 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 PLAINTEXT. */
+void otrl_context_forget(ConnContext *context);
+
+/* Forget all the contexts in a given OtrlUserState. */
+void otrl_context_forget_all(OtrlUserState us);
+
+#endif
diff --git a/libotr-3.2.0/src/dh.c b/libotr-3.2.0/src/dh.c
new file mode 100644
index 0000000..610c84e
--- /dev/null
+++ b/libotr-3.2.0/src/dh.c
@@ -0,0 +1,466 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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_MODULUS_MINUS_2 = 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);
+ DH1536_MODULUS_MINUS_2 = gcry_mpi_new(DH1536_MOD_LEN_BITS);
+ gcry_mpi_sub_ui(DH1536_MODULUS_MINUS_2, DH1536_MODULUS, 2);
+}
+
+/*
+ * Initialize the fields of a DH keypair.
+ */
+void otrl_dh_keypair_init(DH_keypair *kp)
+{
+ kp->groupid = 0;
+ kp->priv = NULL;
+ kp->pub = NULL;
+}
+
+/*
+ * Copy a DH_keypair.
+ */
+void otrl_dh_keypair_copy(DH_keypair *dst, const DH_keypair *src)
+{
+ dst->groupid = src->groupid;
+ dst->priv = gcry_mpi_copy(src->priv);
+ dst->pub = gcry_mpi_copy(src->pub);
+}
+
+/*
+ * 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, const 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);
+
+ hashdata = gcry_malloc_secure(20);
+ if (!hashdata) {
+ gcry_free(gabdata);
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+
+ /* Are we the "high" or "low" end of the connection? */
+ if ( gcry_mpi_cmp(kp->pub, y) > 0 ) {
+ sendbyte = 0x01;
+ rcvbyte = 0x02;
+ } else {
+ 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;
+}
+
+/*
+ * Compute the secure session id, two encryption keys, and four MAC keys
+ * given our DH key and their DH public key.
+ */
+gcry_error_t otrl_dh_compute_v2_auth_keys(const DH_keypair *our_dh,
+ gcry_mpi_t their_pub, unsigned char *sessionid, size_t *sessionidlenp,
+ gcry_cipher_hd_t *enc_c, gcry_cipher_hd_t *enc_cp,
+ gcry_md_hd_t *mac_m1, gcry_md_hd_t *mac_m1p,
+ gcry_md_hd_t *mac_m2, gcry_md_hd_t *mac_m2p)
+{
+ gcry_mpi_t s;
+ size_t slen;
+ unsigned char *sdata;
+ unsigned char *hashdata;
+ unsigned char ctr[16];
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+
+ *enc_c = NULL;
+ *enc_cp = NULL;
+ *mac_m1 = NULL;
+ *mac_m1p = NULL;
+ *mac_m2 = NULL;
+ *mac_m2p = NULL;
+ memset(ctr, 0, 16);
+
+ if (our_dh->groupid != DH1536_GROUP_ID) {
+ /* Invalid group id */
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
+
+ /* Check that their_pub is in range */
+ if (gcry_mpi_cmp_ui(their_pub, 2) < 0 ||
+ gcry_mpi_cmp(their_pub, DH1536_MODULUS_MINUS_2) > 0) {
+ /* Invalid pubkey */
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
+
+ /* Calculate the shared secret MPI */
+ s = gcry_mpi_new(DH1536_MOD_LEN_BITS);
+ gcry_mpi_powm(s, their_pub, our_dh->priv, DH1536_MODULUS);
+
+ /* Output it in the right format */
+ gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &slen, s);
+ sdata = gcry_malloc_secure(slen + 5);
+ if (!sdata) {
+ gcry_mpi_release(s);
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+ sdata[1] = (slen >> 24) & 0xff;
+ sdata[2] = (slen >> 16) & 0xff;
+ sdata[3] = (slen >> 8) & 0xff;
+ sdata[4] = slen & 0xff;
+ gcry_mpi_print(GCRYMPI_FMT_USG, sdata+5, slen, NULL, s);
+ gcry_mpi_release(s);
+
+ /* Calculate the session id */
+ hashdata = gcry_malloc_secure(32);
+ if (!hashdata) {
+ gcry_free(sdata);
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+ sdata[0] = 0x00;
+ gcry_md_hash_buffer(GCRY_MD_SHA256, hashdata, sdata, slen+5);
+ memmove(sessionid, hashdata, 8);
+ *sessionidlenp = 8;
+
+ /* Calculate the encryption keys */
+ sdata[0] = 0x01;
+ gcry_md_hash_buffer(GCRY_MD_SHA256, hashdata, sdata, slen+5);
+
+ err = gcry_cipher_open(enc_c, GCRY_CIPHER_AES,
+ GCRY_CIPHER_MODE_CTR, GCRY_CIPHER_SECURE);
+ if (err) goto err;
+ err = gcry_cipher_setkey(*enc_c, hashdata, 16);
+ if (err) goto err;
+ err = gcry_cipher_setctr(*enc_c, ctr, 16);
+ if (err) goto err;
+
+ err = gcry_cipher_open(enc_cp, GCRY_CIPHER_AES,
+ GCRY_CIPHER_MODE_CTR, GCRY_CIPHER_SECURE);
+ if (err) goto err;
+ err = gcry_cipher_setkey(*enc_cp, hashdata+16, 16);
+ if (err) goto err;
+ err = gcry_cipher_setctr(*enc_cp, ctr, 16);
+ if (err) goto err;
+
+ /* Calculate the MAC keys */
+ sdata[0] = 0x02;
+ gcry_md_hash_buffer(GCRY_MD_SHA256, hashdata, sdata, slen+5);
+ err = gcry_md_open(mac_m1, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
+ if (err) goto err;
+ err = gcry_md_setkey(*mac_m1, hashdata, 32);
+ if (err) goto err;
+
+ sdata[0] = 0x03;
+ gcry_md_hash_buffer(GCRY_MD_SHA256, hashdata, sdata, slen+5);
+ err = gcry_md_open(mac_m2, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
+ if (err) goto err;
+ err = gcry_md_setkey(*mac_m2, hashdata, 32);
+ if (err) goto err;
+
+ sdata[0] = 0x04;
+ gcry_md_hash_buffer(GCRY_MD_SHA256, hashdata, sdata, slen+5);
+ err = gcry_md_open(mac_m1p, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
+ if (err) goto err;
+ err = gcry_md_setkey(*mac_m1p, hashdata, 32);
+ if (err) goto err;
+
+ sdata[0] = 0x05;
+ gcry_md_hash_buffer(GCRY_MD_SHA256, hashdata, sdata, slen+5);
+ err = gcry_md_open(mac_m2p, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
+ if (err) goto err;
+ err = gcry_md_setkey(*mac_m2p, hashdata, 32);
+ if (err) goto err;
+
+ gcry_free(sdata);
+ gcry_free(hashdata);
+ return gcry_error(GPG_ERR_NO_ERROR);
+
+err:
+ gcry_cipher_close(*enc_c);
+ gcry_cipher_close(*enc_cp);
+ gcry_md_close(*mac_m1);
+ gcry_md_close(*mac_m1p);
+ gcry_md_close(*mac_m2);
+ gcry_md_close(*mac_m2p);
+ *enc_c = NULL;
+ *enc_cp = NULL;
+ *mac_m1 = NULL;
+ *mac_m1p = NULL;
+ *mac_m2 = NULL;
+ *mac_m2p = NULL;
+ gcry_free(sdata);
+ gcry_free(hashdata);
+ return err;
+}
+
+/*
+ * Compute the secure session id, given our DH key and their DH public
+ * key.
+ */
+gcry_error_t otrl_dh_compute_v1_session_id(const DH_keypair *our_dh,
+ gcry_mpi_t their_pub, unsigned char *sessionid, size_t *sessionidlenp,
+ OtrlSessionIdHalf *halfp)
+{
+ gcry_mpi_t s;
+ size_t slen;
+ unsigned char *sdata;
+ unsigned char *hashdata;
+
+ if (our_dh->groupid != DH1536_GROUP_ID) {
+ /* Invalid group id */
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
+
+ /* Check that their_pub is in range */
+ if (gcry_mpi_cmp_ui(their_pub, 2) < 0 ||
+ gcry_mpi_cmp(their_pub, DH1536_MODULUS_MINUS_2) > 0) {
+ /* Invalid pubkey */
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
+
+ /* Calculate the shared secret MPI */
+ s = gcry_mpi_new(DH1536_MOD_LEN_BITS);
+ gcry_mpi_powm(s, their_pub, our_dh->priv, DH1536_MODULUS);
+
+ /* Output it in the right format */
+ gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &slen, s);
+ sdata = gcry_malloc_secure(slen + 5);
+ if (!sdata) {
+ gcry_mpi_release(s);
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+ sdata[1] = (slen >> 24) & 0xff;
+ sdata[2] = (slen >> 16) & 0xff;
+ sdata[3] = (slen >> 8) & 0xff;
+ sdata[4] = slen & 0xff;
+ gcry_mpi_print(GCRYMPI_FMT_USG, sdata+5, slen, NULL, s);
+ gcry_mpi_release(s);
+
+ /* Calculate the session id */
+ hashdata = gcry_malloc_secure(20);
+ if (!hashdata) {
+ gcry_free(sdata);
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+ sdata[0] = 0x00;
+ gcry_md_hash_buffer(GCRY_MD_SHA1, hashdata, sdata, slen+5);
+ memmove(sessionid, hashdata, 20);
+ *sessionidlenp = 20;
+
+ /* Which half should be bold? */
+ if (gcry_mpi_cmp(our_dh->pub, their_pub) > 0) {
+ *halfp = OTRL_SESSIONID_SECOND_HALF_BOLD;
+ } else {
+ *halfp = OTRL_SESSIONID_FIRST_HALF_BOLD;
+ }
+
+ gcry_free(hashdata);
+ gcry_free(sdata);
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/*
+ * 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->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(const unsigned char *ctr1, const 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/libotr-3.2.0/src/dh.h b/libotr-3.2.0/src/dh.h
new file mode 100644
index 0000000..d68328f
--- /dev/null
+++ b/libotr-3.2.0/src/dh.h
@@ -0,0 +1,119 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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;
+
+/* Which half of the secure session id should be shown in bold? */
+typedef enum {
+ OTRL_SESSIONID_FIRST_HALF_BOLD,
+ OTRL_SESSIONID_SECOND_HALF_BOLD
+} OtrlSessionIdHalf;
+
+typedef struct {
+ 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);
+
+/*
+ * Initialize the fields of a DH keypair.
+ */
+void otrl_dh_keypair_init(DH_keypair *kp);
+
+/*
+ * Copy a DH_keypair.
+ */
+void otrl_dh_keypair_copy(DH_keypair *dst, const DH_keypair *src);
+
+/*
+ * 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, const DH_keypair *kp,
+ gcry_mpi_t y);
+
+/*
+ * Compute the secure session id, two encryption keys, and four MAC keys
+ * given our DH key and their DH public key.
+ */
+gcry_error_t otrl_dh_compute_v2_auth_keys(const DH_keypair *our_dh,
+ gcry_mpi_t their_pub, unsigned char *sessionid, size_t *sessionidlenp,
+ gcry_cipher_hd_t *enc_c, gcry_cipher_hd_t *enc_cp,
+ gcry_md_hd_t *mac_m1, gcry_md_hd_t *mac_m1p,
+ gcry_md_hd_t *mac_m2, gcry_md_hd_t *mac_m2p);
+
+/*
+ * Compute the secure session id, given our DH key and their DH public
+ * key.
+ */
+gcry_error_t otrl_dh_compute_v1_session_id(const DH_keypair *our_dh,
+ gcry_mpi_t their_pub, unsigned char *sessionid, size_t *sessionidlenp,
+ OtrlSessionIdHalf *halfp);
+
+/*
+ * 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(const unsigned char *ctr1, const unsigned char *ctr2);
+
+#endif
diff --git a/libotr-3.2.0/src/mem.c b/libotr-3.2.0/src/mem.c
new file mode 100644
index 0000000..b2a2191
--- /dev/null
+++ b/libotr-3.2.0/src/mem.c
@@ -0,0 +1,163 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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 (void *)((char *)p + header_size);
+}
+
+static int otrl_mem_is_secure(const void *p)
+{
+ return 1;
+}
+
+static void otrl_mem_free(void *p)
+{
+ void *real_p = (void *)((char *)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 = (void *)((char *)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 = (void *)((char *)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 (void *)((char *)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/libotr-3.2.0/src/mem.h b/libotr-3.2.0/src/mem.h
new file mode 100644
index 0000000..19e00cf
--- /dev/null
+++ b/libotr-3.2.0/src/mem.h
@@ -0,0 +1,25 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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/libotr-3.2.0/src/message.c b/libotr-3.2.0/src/message.c
new file mode 100644
index 0000000..704bb5c
--- /dev/null
+++ b/libotr-3.2.0/src/message.c
@@ -0,0 +1,1446 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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 "auth.h"
+#include "message.h"
+#include "sm.h"
+
+/* The API version */
+extern unsigned int otrl_api_version;
+
+/* How long after sending a packet should we wait to send a heartbeat? */
+#define HEARTBEAT_INTERVAL 60
+
+/* How old are messages allowed to be in order to be candidates for
+ * resending in response to a rekey? */
+#define RESEND_INTERVAL 60
+
+/* 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,
+ const 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_VERSION_MASK) == 0) {
+ return gcry_error(GPG_ERR_NO_ERROR);
+ }
+
+ /* If this is an OTR Query message, don't encrypt it. */
+ if (otrl_proto_message_type(message) == OTRL_MSGTYPE_QUERY) {
+ /* Replace the "?OTR?" with a custom message */
+ char *bettermsg = otrl_proto_default_query_msg(accountname, policy);
+ if (bettermsg) {
+ *messagep = bettermsg;
+ }
+ return gcry_error(GPG_ERR_NO_ERROR);
+ }
+
+ /* What is the current message disposition? */
+ switch(context->msgstate) {
+ case OTRL_MSGSTATE_PLAINTEXT:
+ if ((policy & OTRL_POLICY_REQUIRE_ENCRYPTION)) {
+ /* We're trying to send an unencrypted message with a policy
+ * that disallows that. Don't do that, but try to start
+ * up OTR instead. */
+ if ((!(ops->display_otr_message) ||
+ ops->display_otr_message(opdata, accountname,
+ protocol, recipient, "Attempting to start a "
+ "private conversation...")) && 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,
+ policy);
+ strcpy(context->lastmessage, message);
+ context->lastsent = time(NULL);
+ context->may_retransmit = 2;
+ if (bettermsg) {
+ *messagep = bettermsg;
+ } else {
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+ }
+ } else {
+ if ((policy & OTRL_POLICY_SEND_WHITESPACE_TAG) &&
+ 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);
+ size_t basetaglen = strlen(OTRL_MESSAGE_TAG_BASE);
+ size_t v1taglen = (policy & OTRL_POLICY_ALLOW_V1) ?
+ strlen(OTRL_MESSAGE_TAG_V1) : 0;
+ size_t v2taglen = (policy & OTRL_POLICY_ALLOW_V2) ?
+ strlen(OTRL_MESSAGE_TAG_V2) : 0;
+ char *taggedmsg = malloc(msglen + basetaglen + v1taglen
+ +v2taglen + 1);
+ if (taggedmsg) {
+ strcpy(taggedmsg, message);
+ strcpy(taggedmsg + msglen, OTRL_MESSAGE_TAG_BASE);
+ if (v1taglen) {
+ strcpy(taggedmsg + msglen + basetaglen,
+ OTRL_MESSAGE_TAG_V1);
+ }
+ if (v2taglen) {
+ strcpy(taggedmsg + msglen + basetaglen + v1taglen,
+ OTRL_MESSAGE_TAG_V2);
+ }
+ *messagep = taggedmsg;
+ if (context) {
+ context->otr_offer = OFFER_SENT;
+ }
+ }
+ }
+ }
+ break;
+ case OTRL_MSGSTATE_ENCRYPTED:
+ /* Create the new, encrypted message */
+ err = otrl_proto_create_data(&msgtosend, context, message, tlvs,
+ 0);
+ 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->display_otr_message) ||
+ ops->display_otr_message(opdata, accountname,
+ protocol, recipient, "An error occurred when "
+ "encrypting your message. The message was not "
+ "sent.")) && 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);
+ }
+ }
+ break;
+ case OTRL_MSGSTATE_FINISHED:
+ *messagep = strdup("");
+ if ((!(ops->display_otr_message) ||
+ ops->display_otr_message(opdata, accountname,
+ protocol, recipient, "Your message was not sent. "
+ "Either end your private conversation, or restart "
+ "it.")) && ops->notify) {
+ const char *fmt = "%s has already closed his/her 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);
+ }
+ break;
+ }
+
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* If err == 0, send the last auth message for the given context to the
+ * appropriate user. Otherwise, display an appripriate error dialog.
+ * Return the value of err that was passed. */
+static gcry_error_t send_or_error_auth(const OtrlMessageAppOps *ops,
+ void *opdata, gcry_error_t err, ConnContext *context)
+{
+ if (!err) {
+ const char *msg = context->auth.lastauthmsg;
+ if (msg && *msg) {
+ otrl_message_fragment_and_send(ops, opdata, context, msg, OTRL_FRAGMENT_SEND_ALL, NULL);
+ /*if (ops->inject_message) {
+ ops->inject_message(opdata, context->accountname,
+ context->protocol, context->username, msg);
+ }*/
+ }
+ } else {
+ const char *buf_format = "Error setting up private conversation: %s";
+ const char *strerr;
+ char *buf;
+
+ switch(gcry_err_code(err)) {
+ case GPG_ERR_INV_VALUE:
+ strerr = "Malformed message received";
+ break;
+ default:
+ strerr = gcry_strerror(err);
+ break;
+ }
+ buf = malloc(strlen(buf_format) + strlen(strerr) - 1);
+ if (buf) {
+ sprintf(buf, buf_format, strerr);
+ }
+ if ((!(ops->display_otr_message) ||
+ ops->display_otr_message(opdata, context->accountname,
+ context->protocol, context->username, buf))
+ && ops->notify) {
+ ops->notify(opdata, OTRL_NOTIFY_ERROR, context->accountname,
+ context->protocol, context->username, "OTR error",
+ buf, NULL);
+ }
+ free(buf);
+ }
+ return err;
+}
+
+typedef struct {
+ int gone_encrypted;
+ OtrlUserState us;
+ const OtrlMessageAppOps *ops;
+ void *opdata;
+ ConnContext *context;
+ int ignore_message;
+ char **messagep;
+} EncrData;
+
+static gcry_error_t go_encrypted(const OtrlAuthInfo *auth, void *asdata)
+{
+ EncrData *edata = asdata;
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+ Fingerprint *found_print = NULL;
+ int fprint_added = 0;
+ OtrlMessageState oldstate = edata->context->msgstate;
+ Fingerprint *oldprint = edata->context->active_fingerprint;
+
+ /* See if we're talking to ourselves */
+ if (!gcry_mpi_cmp(auth->their_pub, auth->our_dh.pub)) {
+ /* Yes, we are. */
+ if ((!(edata->ops->display_otr_message) ||
+ edata->ops->display_otr_message(edata->opdata,
+ edata->context->accountname, edata->context->protocol,
+ edata->context->username,
+ "We are receiving our own OTR messages. "
+ "You are either trying to talk to yourself, "
+ "or someone is reflecting your messages back "
+ "at you.")) && edata->ops->notify) {
+ edata->ops->notify(edata->opdata, OTRL_NOTIFY_ERROR,
+ edata->context->accountname, edata->context->protocol,
+ edata->context->username, "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.");
+ }
+ edata->ignore_message = 1;
+ return gcry_error(GPG_ERR_NO_ERROR);
+ }
+
+ found_print = otrl_context_find_fingerprint(edata->context,
+ edata->context->auth.their_fingerprint, 1, &fprint_added);
+
+ if (fprint_added) {
+ /* Inform the user of the new fingerprint */
+ if (edata->ops->new_fingerprint) {
+ edata->ops->new_fingerprint(edata->opdata, edata->us,
+ edata->context->accountname, edata->context->protocol,
+ edata->context->username,
+ edata->context->auth.their_fingerprint);
+ }
+ /* Arrange that the new fingerprint be written to disk */
+ if (edata->ops->write_fingerprints) {
+ edata->ops->write_fingerprints(edata->opdata);
+ }
+ }
+
+ /* Is this a new session or just a refresh of an existing one? */
+ if (edata->context->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
+ oldprint == found_print &&
+ edata->context->our_keyid - 1 == edata->context->auth.our_keyid &&
+ !gcry_mpi_cmp(edata->context->our_old_dh_key.pub,
+ edata->context->auth.our_dh.pub) &&
+ ((edata->context->their_keyid > 0 &&
+ edata->context->their_keyid ==
+ edata->context->auth.their_keyid &&
+ !gcry_mpi_cmp(edata->context->their_y,
+ edata->context->auth.their_pub)) ||
+ (edata->context->their_keyid > 1 &&
+ edata->context->their_keyid - 1 ==
+ edata->context->auth.their_keyid &&
+ edata->context->their_old_y != NULL &&
+ !gcry_mpi_cmp(edata->context->their_old_y,
+ edata->context->auth.their_pub)))) {
+ /* This is just a refresh of the existing session. */
+ if (edata->ops->still_secure) {
+ edata->ops->still_secure(edata->opdata, edata->context,
+ edata->context->auth.initiated);
+ }
+ edata->ignore_message = 1;
+ return gcry_error(GPG_ERR_NO_ERROR);
+ }
+
+ /* Copy the information from the auth into the context */
+ memmove(edata->context->sessionid,
+ edata->context->auth.secure_session_id, 20);
+ edata->context->sessionid_len =
+ edata->context->auth.secure_session_id_len;
+ edata->context->sessionid_half =
+ edata->context->auth.session_id_half;
+ edata->context->protocol_version =
+ edata->context->auth.protocol_version;
+
+ edata->context->their_keyid = edata->context->auth.their_keyid;
+ gcry_mpi_release(edata->context->their_y);
+ gcry_mpi_release(edata->context->their_old_y);
+ edata->context->their_y = gcry_mpi_copy(edata->context->auth.their_pub);
+ edata->context->their_old_y = NULL;
+
+ if (edata->context->our_keyid - 1 != edata->context->auth.our_keyid ||
+ gcry_mpi_cmp(edata->context->our_old_dh_key.pub,
+ edata->context->auth.our_dh.pub)) {
+ otrl_dh_keypair_free(&(edata->context->our_dh_key));
+ otrl_dh_keypair_free(&(edata->context->our_old_dh_key));
+ otrl_dh_keypair_copy(&(edata->context->our_old_dh_key),
+ &(edata->context->auth.our_dh));
+ otrl_dh_gen_keypair(edata->context->our_old_dh_key.groupid,
+ &(edata->context->our_dh_key));
+ edata->context->our_keyid = edata->context->auth.our_keyid + 1;
+ }
+
+ /* Create the session keys from the DH keys */
+ otrl_dh_session_free(&(edata->context->sesskeys[0][0]));
+ err = otrl_dh_session(&(edata->context->sesskeys[0][0]),
+ &(edata->context->our_dh_key), edata->context->their_y);
+ if (err) return err;
+ otrl_dh_session_free(&(edata->context->sesskeys[1][0]));
+ err = otrl_dh_session(&(edata->context->sesskeys[1][0]),
+ &(edata->context->our_old_dh_key), edata->context->their_y);
+ if (err) return err;
+
+ edata->context->generation++;
+ edata->context->active_fingerprint = found_print;
+ edata->context->msgstate = OTRL_MSGSTATE_ENCRYPTED;
+
+ if (edata->ops->update_context_list) {
+ edata->ops->update_context_list(edata->opdata);
+ }
+ if (oldstate == OTRL_MSGSTATE_ENCRYPTED && oldprint == found_print) {
+ if (edata->ops->still_secure) {
+ edata->ops->still_secure(edata->opdata, edata->context,
+ edata->context->auth.initiated);
+ }
+ } else {
+ if (edata->ops->gone_secure) {
+ edata->ops->gone_secure(edata->opdata, edata->context);
+ }
+ }
+
+ edata->gone_encrypted = 1;
+
+ return gpg_error(GPG_ERR_NO_ERROR);
+}
+
+static void maybe_resend(EncrData *edata)
+{
+ gcry_error_t err;
+ time_t now;
+
+ if (!edata->gone_encrypted) return;
+
+ /* See if there's a message we sent recently that should be resent. */
+ now = time(NULL);
+ if (edata->context->lastmessage != NULL &&
+ edata->context->may_retransmit &&
+ edata->context->lastsent >= (now - RESEND_INTERVAL)) {
+ char *resendmsg;
+ int resending = (edata->context->may_retransmit == 1);
+
+ /* Re-encrypt the message with the new keys */
+ err = otrl_proto_create_data(&resendmsg,
+ edata->context, edata->context->lastmessage, NULL, 0);
+ if (!err) {
+ const char *format = "<b>The last message "
+ "to %s was resent.</b>";
+ char *buf;
+
+ /* Resend the message */
+ otrl_message_fragment_and_send(edata->ops, edata->opdata, edata->context, resendmsg, OTRL_FRAGMENT_SEND_ALL, NULL);
+ free(resendmsg);
+ edata->context->lastsent = now;
+
+ if (!resending) {
+ /* We're actually just sending it
+ * for the first time. */
+ edata->ignore_message = 1;
+ } else {
+ /* Let the user know we resent it */
+ buf = malloc(strlen(format) +
+ strlen(edata->context->username) - 1);
+ if (buf) {
+ sprintf(buf, format, edata->context->username);
+ if (edata->ops->display_otr_message) {
+ if (!edata->ops->display_otr_message(
+ edata->opdata, edata->context->accountname,
+ edata->context->protocol,
+ edata->context->username, buf)) {
+ edata->ignore_message = 1;
+ }
+ }
+ if (edata->ignore_message != 1) {
+ *(edata->messagep) = buf;
+ edata->ignore_message = 0;
+ } else {
+ free(buf);
+ }
+ }
+ }
+ }
+ }
+}
+
+/* Set the trust level based on the result of the SMP */
+static void set_smp_trust(const OtrlMessageAppOps *ops, void *opdata,
+ ConnContext *context, int trusted)
+{
+ otrl_context_set_trust(context->active_fingerprint, trusted ? "smp" : "");
+
+ /* Write the new info to disk, redraw the ui, and redraw the
+ * OTR buttons. */
+ if (ops->write_fingerprints) {
+ ops->write_fingerprints(opdata);
+ }
+}
+
+static void init_respond_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
+ void *opdata, ConnContext *context, const char *question,
+ const unsigned char *secret, size_t secretlen, int initiating)
+{
+ unsigned char *smpmsg = NULL;
+ int smpmsglen;
+ unsigned char combined_secret[SM_DIGEST_SIZE];
+ gcry_error_t err;
+ unsigned char our_fp[20];
+ unsigned char *combined_buf;
+ size_t combined_buf_len;
+ OtrlTLV *sendtlv;
+ char *sendsmp = NULL;
+
+ if (!context || context->msgstate != OTRL_MSGSTATE_ENCRYPTED) return;
+
+ /*
+ * Construct the combined secret as a SHA256 hash of:
+ * Version byte (0x01), Initiator fingerprint (20 bytes),
+ * responder fingerprint (20 bytes), secure session id, input secret
+ */
+ otrl_privkey_fingerprint_raw(us, our_fp, context->accountname,
+ context->protocol);
+
+ combined_buf_len = 41 + context->sessionid_len + secretlen;
+ combined_buf = malloc(combined_buf_len);
+ combined_buf[0] = 0x01;
+ if (initiating) {
+ memmove(combined_buf + 1, our_fp, 20);
+ memmove(combined_buf + 21,
+ context->active_fingerprint->fingerprint, 20);
+ } else {
+ memmove(combined_buf + 1,
+ context->active_fingerprint->fingerprint, 20);
+ memmove(combined_buf + 21, our_fp, 20);
+ }
+ memmove(combined_buf + 41, context->sessionid,
+ context->sessionid_len);
+ memmove(combined_buf + 41 + context->sessionid_len,
+ secret, secretlen);
+ gcry_md_hash_buffer(SM_HASH_ALGORITHM, combined_secret, combined_buf,
+ combined_buf_len);
+ free(combined_buf);
+
+ if (initiating) {
+ otrl_sm_step1(context->smstate, combined_secret, SM_DIGEST_SIZE,
+ &smpmsg, &smpmsglen);
+ } else {
+ otrl_sm_step2b(context->smstate, combined_secret, SM_DIGEST_SIZE,
+ &smpmsg, &smpmsglen);
+ }
+
+ /* If we've got a question, attach it to the smpmsg */
+ if (question != NULL) {
+ size_t qlen = strlen(question);
+ unsigned char *qsmpmsg = malloc(qlen + 1 + smpmsglen);
+ if (!qsmpmsg) {
+ free(smpmsg);
+ return;
+ }
+ strcpy((char *)qsmpmsg, question);
+ memmove(qsmpmsg + qlen + 1, smpmsg, smpmsglen);
+ free(smpmsg);
+ smpmsg = qsmpmsg;
+ smpmsglen += qlen + 1;
+ }
+
+ /* Send msg with next smp msg content */
+ sendtlv = otrl_tlv_new(initiating ?
+ (question != NULL ? OTRL_TLV_SMP1Q : OTRL_TLV_SMP1)
+ : OTRL_TLV_SMP2,
+ smpmsglen, smpmsg);
+ err = otrl_proto_create_data(&sendsmp, context, "", sendtlv,
+ OTRL_MSGFLAGS_IGNORE_UNREADABLE);
+ if (!err) {
+ /* Send it, and set the next expected message to the
+ * logical response */
+ err = otrl_message_fragment_and_send(ops, opdata, context,
+ sendsmp, OTRL_FRAGMENT_SEND_ALL, NULL);
+ context->smstate->nextExpected =
+ initiating ? OTRL_SMP_EXPECT2 : OTRL_SMP_EXPECT3;
+ }
+ free(sendsmp);
+ otrl_tlv_free(sendtlv);
+ free(smpmsg);
+}
+
+/* Initiate the Socialist Millionaires' Protocol */
+void otrl_message_initiate_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
+ void *opdata, ConnContext *context, const unsigned char *secret,
+ size_t secretlen)
+{
+ init_respond_smp(us, ops, opdata, context, NULL, secret, secretlen, 1);
+}
+
+/* Initiate the Socialist Millionaires' Protocol and send a prompt
+ * question to the buddy */
+void otrl_message_initiate_smp_q(OtrlUserState us,
+ const OtrlMessageAppOps *ops, void *opdata, ConnContext *context,
+ const char *question, const unsigned char *secret, size_t secretlen)
+{
+ init_respond_smp(us, ops, opdata, context, question, secret, secretlen, 1);
+}
+
+/* Respond to a buddy initiating the Socialist Millionaires' Protocol */
+void otrl_message_respond_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
+ void *opdata, ConnContext *context, const unsigned char *secret,
+ size_t secretlen)
+{
+ init_respond_smp(us, ops, opdata, context, NULL, secret, secretlen, 0);
+}
+
+/* Abort the SMP. Called when an unexpected SMP message breaks the
+ * normal flow. */
+void otrl_message_abort_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
+ void *opdata, ConnContext *context)
+{
+ OtrlTLV *sendtlv = otrl_tlv_new(OTRL_TLV_SMP_ABORT, 0,
+ (const unsigned char *)"");
+ char *sendsmp = NULL;
+ gcry_error_t err;
+
+ context->smstate->nextExpected = OTRL_SMP_EXPECT1;
+
+ err = otrl_proto_create_data(&sendsmp,
+ context, "", sendtlv,
+ OTRL_MSGFLAGS_IGNORE_UNREADABLE);
+ if (!err) {
+ /* Send the abort signal so our buddy knows we've stopped */
+ err = otrl_message_fragment_and_send(ops, opdata, context,
+ sendsmp, OTRL_FRAGMENT_SEND_ALL, NULL);
+ }
+ free(sendsmp);
+ otrl_tlv_free(sendtlv);
+}
+
+/* Handle a message just received from the network. It is safe to pass
+ * all received messages to this routine. add_appdata is a function
+ * that will be called in the event that a new ConnContext is created.
+ * 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, const 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;
+ OtrlMessageType msgtype;
+ int context_added = 0;
+ OtrlMessageState msgstate;
+ OtrlPolicy policy = OTRL_POLICY_DEFAULT;
+ int fragment_assembled = 0;
+ char *unfragmessage = NULL;
+ EncrData edata;
+
+ 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_VERSION_MASK) == 0) {
+ return 0;
+ }
+
+ /* See if we have a fragment */
+ switch(otrl_proto_fragment_accumulate(&unfragmessage, context, message)) {
+ case OTRL_FRAGMENT_UNFRAGMENTED:
+ /* Do nothing */
+ break;
+ case OTRL_FRAGMENT_INCOMPLETE:
+ /* We've accumulated this fragment, but we don't have a
+ * complete message yet */
+ return 1;
+ case OTRL_FRAGMENT_COMPLETE:
+ /* We've got a new complete message, in unfragmessage. */
+ fragment_assembled = 1;
+ message = unfragmessage;
+ break;
+ }
+
+ /* 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);
+ msgstate = context->msgstate;
+
+ /* See if they responded to our OTR offer */
+ if ((policy & OTRL_POLICY_SEND_WHITESPACE_TAG)) {
+ if (msgtype != OTRL_MSGTYPE_NOTOTR) {
+ context->otr_offer = OFFER_ACCEPTED;
+ } else if (context->otr_offer == OFFER_SENT) {
+ context->otr_offer = OFFER_REJECTED;
+ }
+ }
+
+ edata.gone_encrypted = 0;
+ edata.us = us;
+ edata.context = context;
+ edata.ops = ops;
+ edata.opdata = opdata;
+ edata.ignore_message = -1;
+ edata.messagep = newmessagep;
+
+ switch(msgtype) {
+ unsigned int bestversion;
+ const char *startwhite, *endwhite;
+ DH_keypair *our_dh;
+ unsigned int our_keyid;
+ OtrlPrivKey *privkey;
+ gcry_error_t err;
+ int haveauthmsg;
+ case OTRL_MSGTYPE_QUERY:
+ /* See if we should use an existing DH keypair, or generate
+ * a fresh one. */
+ if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
+ our_dh = &(context->our_old_dh_key);
+ our_keyid = context->our_keyid - 1;
+ } else {
+ our_dh = NULL;
+ our_keyid = 0;
+ }
+
+ /* Find the best version of OTR that we both speak */
+ switch(otrl_proto_query_bestversion(message, policy)) {
+ case 2:
+ err = otrl_auth_start_v2(&(context->auth));
+ send_or_error_auth(ops, opdata, err, context);
+ break;
+ case 1:
+ /* Get our private key */
+ privkey = otrl_privkey_find(us, context->accountname,
+ context->protocol);
+ if (privkey == NULL) {
+ /* We've got no private key! */
+ if (ops->create_privkey) {
+ ops->create_privkey(opdata, context->accountname,
+ context->protocol);
+ privkey = otrl_privkey_find(us,
+ context->accountname, context->protocol);
+ }
+ }
+ if (privkey) {
+ err = otrl_auth_start_v1(&(context->auth), our_dh,
+ our_keyid, privkey);
+ send_or_error_auth(ops, opdata, err, context);
+ }
+ break;
+ default:
+ /* Just ignore this message */
+ break;
+ }
+ /* Don't display the Query message to the user. */
+ if (edata.ignore_message == -1) edata.ignore_message = 1;
+ break;
+
+ case OTRL_MSGTYPE_DH_COMMIT:
+ if ((policy & OTRL_POLICY_ALLOW_V2)) {
+ err = otrl_auth_handle_commit(&(context->auth), message);
+ send_or_error_auth(ops, opdata, err, context);
+ }
+
+ if (edata.ignore_message == -1) edata.ignore_message = 1;
+ break;
+
+ case OTRL_MSGTYPE_DH_KEY:
+ if ((policy & OTRL_POLICY_ALLOW_V2)) {
+ /* Get our private key */
+ privkey = otrl_privkey_find(us, context->accountname,
+ context->protocol);
+ if (privkey == NULL) {
+ /* We've got no private key! */
+ if (ops->create_privkey) {
+ ops->create_privkey(opdata, context->accountname,
+ context->protocol);
+ privkey = otrl_privkey_find(us,
+ context->accountname, context->protocol);
+ }
+ }
+ if (privkey) {
+ err = otrl_auth_handle_key(&(context->auth), message,
+ &haveauthmsg, privkey);
+ if (err || haveauthmsg) {
+ send_or_error_auth(ops, opdata, err, context);
+ }
+ }
+ }
+
+ if (edata.ignore_message == -1) edata.ignore_message = 1;
+ break;
+
+ case OTRL_MSGTYPE_REVEALSIG:
+ if ((policy & OTRL_POLICY_ALLOW_V2)) {
+ /* Get our private key */
+ privkey = otrl_privkey_find(us, context->accountname,
+ context->protocol);
+ if (privkey == NULL) {
+ /* We've got no private key! */
+ if (ops->create_privkey) {
+ ops->create_privkey(opdata, context->accountname,
+ context->protocol);
+ privkey = otrl_privkey_find(us,
+ context->accountname, context->protocol);
+ }
+ }
+ if (privkey) {
+ err = otrl_auth_handle_revealsig(&(context->auth),
+ message, &haveauthmsg, privkey, go_encrypted,
+ &edata);
+ if (err || haveauthmsg) {
+ send_or_error_auth(ops, opdata, err, context);
+ maybe_resend(&edata);
+ }
+ }
+ }
+
+ if (edata.ignore_message == -1) edata.ignore_message = 1;
+ break;
+
+ case OTRL_MSGTYPE_SIGNATURE:
+ if ((policy & OTRL_POLICY_ALLOW_V2)) {
+ err = otrl_auth_handle_signature(&(context->auth),
+ message, &haveauthmsg, go_encrypted, &edata);
+ if (err || haveauthmsg) {
+ send_or_error_auth(ops, opdata, err, context);
+ maybe_resend(&edata);
+ }
+ }
+
+ if (edata.ignore_message == -1) edata.ignore_message = 1;
+ break;
+
+ case OTRL_MSGTYPE_V1_KEYEXCH:
+ if ((policy & OTRL_POLICY_ALLOW_V1)) {
+ /* See if we should use an existing DH keypair, or generate
+ * a fresh one. */
+ if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
+ our_dh = &(context->our_old_dh_key);
+ our_keyid = context->our_keyid - 1;
+ } else {
+ our_dh = NULL;
+ our_keyid = 0;
+ }
+
+ /* Get our private key */
+ privkey = otrl_privkey_find(us, context->accountname,
+ context->protocol);
+ if (privkey == NULL) {
+ /* We've got no private key! */
+ if (ops->create_privkey) {
+ ops->create_privkey(opdata, context->accountname,
+ context->protocol);
+ privkey = otrl_privkey_find(us,
+ context->accountname, context->protocol);
+ }
+ }
+ if (privkey) {
+ err = otrl_auth_handle_v1_key_exchange(&(context->auth),
+ message, &haveauthmsg, privkey, our_dh, our_keyid,
+ go_encrypted, &edata);
+ if (err || haveauthmsg) {
+ send_or_error_auth(ops, opdata, err, context);
+ maybe_resend(&edata);
+ }
+ }
+ }
+
+ if (edata.ignore_message == -1) edata.ignore_message = 1;
+ break;
+
+ case OTRL_MSGTYPE_DATA:
+ switch(context->msgstate) {
+ gcry_error_t err;
+ OtrlTLV *tlvs, *tlv;
+ char *plaintext;
+ char *buf;
+ const char *format;
+ const char *displayaccountname;
+ unsigned char flags;
+ NextExpectedSMP nextMsg;
+
+ case OTRL_MSGSTATE_PLAINTEXT:
+ case OTRL_MSGSTATE_FINISHED:
+ /* See if we're supposed to ignore this message in
+ * the event it's unreadable. */
+ err = otrl_proto_data_read_flags(message, &flags);
+ if ((flags & OTRL_MSGFLAGS_IGNORE_UNREADABLE)) {
+ edata.ignore_message = 1;
+ break;
+ }
+
+ /* 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)) {
+ edata.ignore_message = 1;
+ }
+ }
+ if (edata.ignore_message != 1) {
+ *newmessagep = buf;
+ edata.ignore_message = 0;
+ } else {
+ free(buf);
+ }
+ }
+ format = "?OTR Error: You sent encrypted "
+ "data to %s, who wasn't expecting it.";
+ if (otrl_api_version >= 0x00030100 &&
+ ops->account_name) {
+ displayaccountname = ops->account_name(opdata,
+ context->accountname, protocol);
+ } else {
+ displayaccountname = NULL;
+ }
+ buf = malloc(strlen(format) + strlen(displayaccountname ?
+ displayaccountname : context->accountname)
+ - 1);
+ if (buf) {
+ sprintf(buf, format, displayaccountname ?
+ displayaccountname : context->accountname);
+ if (ops->inject_message) {
+ ops->inject_message(opdata, accountname, protocol,
+ sender, buf);
+ }
+ free(buf);
+ }
+ if (displayaccountname && otrl_api_version >= 0x00030100 &&
+ ops->account_name_free) {
+ ops->account_name_free(opdata, displayaccountname);
+ }
+
+ break;
+
+ case OTRL_MSGSTATE_ENCRYPTED:
+ err = otrl_proto_accept_data(&plaintext, &tlvs, context,
+ message, &flags);
+ if (err) {
+ int is_conflict =
+ (gpg_err_code(err) == GPG_ERR_CONFLICT);
+ if ((flags & OTRL_MSGFLAGS_IGNORE_UNREADABLE)) {
+ edata.ignore_message = 1;
+ break;
+ }
+ format = is_conflict ? "We received an unreadable "
+ "encrypted message from %s." :
+ "We received a malformed data message from %s.";
+ buf = malloc(strlen(format) + strlen(sender) - 1);
+ if (buf) {
+ sprintf(buf, format, sender);
+ if ((!(ops->display_otr_message) ||
+ ops->display_otr_message(opdata,
+ accountname, protocol, sender,
+ buf)) && 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, is_conflict ? "?OTR Error: "
+ "You transmitted an unreadable "
+ "encrypted message." :
+ "?OTR Error: You transmitted "
+ "a malformed data message");
+ }
+ edata.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)) {
+ otrl_context_force_finished(context);
+ }
+
+ /* If TLVs contain SMP data, process it */
+ nextMsg = context->smstate->nextExpected;
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1Q);
+ if (tlv && nextMsg == OTRL_SMP_EXPECT1) {
+ /* We can only do the verification half now.
+ * We must wait for the secret to be entered
+ * to continue. */
+ char *question = (char *)tlv->data;
+ char *qend = memchr(question, '\0', tlv->len - 1);
+ size_t qlen = qend ? (qend - question + 1) : tlv->len;
+ otrl_sm_step2a(context->smstate, tlv->data + qlen,
+ tlv->len - qlen, 1);
+ }
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
+ if (tlv && nextMsg == OTRL_SMP_EXPECT1) {
+ /* We can only do the verification half now.
+ * We must wait for the secret to be entered
+ * to continue. */
+ otrl_sm_step2a(context->smstate, tlv->data, tlv->len,
+ 0);
+ }
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
+ if (tlv && nextMsg == OTRL_SMP_EXPECT2) {
+ unsigned char* nextmsg;
+ int nextmsglen;
+ OtrlTLV *sendtlv;
+ char *sendsmp;
+ otrl_sm_step3(context->smstate, tlv->data, tlv->len,
+ &nextmsg, &nextmsglen);
+
+ if (context->smstate->sm_prog_state !=
+ OTRL_SMP_PROG_CHEATED) {
+ /* Send msg with next smp msg content */
+ sendtlv = otrl_tlv_new(OTRL_TLV_SMP3, nextmsglen,
+ nextmsg);
+ err = otrl_proto_create_data(&sendsmp,
+ context, "", sendtlv,
+ OTRL_MSGFLAGS_IGNORE_UNREADABLE);
+ if (!err) {
+ err = otrl_message_fragment_and_send(ops,
+ opdata, context, sendsmp,
+ OTRL_FRAGMENT_SEND_ALL, NULL);
+ }
+ free(sendsmp);
+ otrl_tlv_free(sendtlv);
+ }
+ free(nextmsg);
+ }
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
+ if (tlv && nextMsg == OTRL_SMP_EXPECT3) {
+ unsigned char* nextmsg;
+ int nextmsglen;
+ OtrlTLV *sendtlv;
+ char *sendsmp;
+ err = otrl_sm_step4(context->smstate, tlv->data,
+ tlv->len, &nextmsg, &nextmsglen);
+ /* Set trust level based on result */
+ if (context->smstate->received_question == 0) {
+ set_smp_trust(ops, opdata, context,
+ (err == gcry_error(GPG_ERR_NO_ERROR)));
+ }
+
+ if (context->smstate->sm_prog_state !=
+ OTRL_SMP_PROG_CHEATED) {
+ /* Send msg with next smp msg content */
+ sendtlv = otrl_tlv_new(OTRL_TLV_SMP4, nextmsglen,
+ nextmsg);
+ err = otrl_proto_create_data(&sendsmp,
+ context, "", sendtlv,
+ OTRL_MSGFLAGS_IGNORE_UNREADABLE);
+ if (!err) {
+ err = otrl_message_fragment_and_send(ops,
+ opdata, context, sendsmp,
+ OTRL_FRAGMENT_SEND_ALL, NULL);
+ }
+ free(sendsmp);
+ otrl_tlv_free(sendtlv);
+ }
+ free(nextmsg);
+ }
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
+ if (tlv && nextMsg == OTRL_SMP_EXPECT4) {
+ err = otrl_sm_step5(context->smstate, tlv->data,
+ tlv->len);
+ /* Set trust level based on result */
+ set_smp_trust(ops, opdata, context,
+ (err == gcry_error(GPG_ERR_NO_ERROR)));
+ }
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT);
+ if (tlv) {
+ context->smstate->nextExpected = OTRL_SMP_EXPECT1;
+ }
+ if (plaintext[0] == '\0') {
+ /* If it's a heartbeat (an empty message), don't
+ * display it to the user, but log a debug message. */
+ 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);
+ }
+ edata.ignore_message = 1;
+ } else if (edata.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,
+ OTRL_MSGFLAGS_IGNORE_UNREADABLE);
+ 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 (edata.ignore_message != 1) {
+ *newmessagep = plaintext;
+ edata.ignore_message = 0;
+ } else {
+ free(plaintext);
+ }
+ break;
+ }
+ break;
+
+ case OTRL_MSGTYPE_ERROR:
+ if ((policy & OTRL_POLICY_ERROR_START_AKE)) {
+ char *msgtosend = otrl_proto_default_query_msg(
+ context->accountname, policy);
+ if (msgtosend && ops->inject_message) {
+ ops->inject_message(opdata, context->accountname,
+ context->protocol, context->username,
+ msgtosend);
+ }
+ free(msgtosend);
+ }
+
+ if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
+ /* Mark the last message we sent as eligible for
+ * retransmission */
+ context->may_retransmit = 1;
+ }
+
+ /* 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)) {
+ edata.ignore_message = 1;
+ }
+ }
+ break;
+
+ case OTRL_MSGTYPE_TAGGEDPLAINTEXT:
+ /* Strip the tag from the message */
+ bestversion = otrl_proto_whitespace_bestversion(message,
+ &startwhite, &endwhite, policy);
+ if (startwhite && endwhite) {
+ size_t restlen = strlen(endwhite);
+ char *strippedmsg = strdup(message);
+
+ if (strippedmsg) {
+ memmove(strippedmsg + (startwhite - message),
+ strippedmsg + (endwhite - message), restlen+1);
+ *newmessagep = strippedmsg;
+ edata.ignore_message = 0;
+ }
+ }
+ if (bestversion && context->msgstate != OTRL_MSGSTATE_ENCRYPTED
+ && (policy & OTRL_POLICY_WHITESPACE_START_AKE)) {
+ switch(bestversion) {
+ case 2:
+ err = otrl_auth_start_v2(&(context->auth));
+ send_or_error_auth(ops, opdata, err, context);
+ break;
+ case 1:
+ /* Get our private key */
+ privkey = otrl_privkey_find(us, context->accountname,
+ context->protocol);
+ if (privkey == NULL) {
+ /* We've got no private key! */
+ if (ops->create_privkey) {
+ ops->create_privkey(opdata,
+ context->accountname,
+ context->protocol);
+ privkey = otrl_privkey_find(us,
+ context->accountname,
+ context->protocol);
+ }
+ }
+ if (privkey) {
+ err = otrl_auth_start_v1(&(context->auth), NULL, 0,
+ privkey);
+ send_or_error_auth(ops, opdata, err, context);
+ }
+ break;
+ default:
+ /* Don't start the AKE */
+ break;
+ }
+ }
+
+ /* FALLTHROUGH */
+ case OTRL_MSGTYPE_NOTOTR:
+ if (context->msgstate != OTRL_MSGSTATE_PLAINTEXT ||
+ (policy & OTRL_POLICY_REQUIRE_ENCRYPTION)) {
+ /* Not fine. Let the user know. */
+
+ /* Don't use g_strdup_printf here, because someone
+ * (not us) is going to free() the *message pointer,
+ * not g_free() it. */
+ const char *plainmsg = (*newmessagep) ? *newmessagep : message;
+ const char *format = "<b>The following message received "
+ "from %s was <i>not</i> encrypted: [</b>%s<b>]</b>";
+ char *buf = malloc(strlen(format) + strlen(context->username)
+ + strlen(plainmsg) - 3);
+ /* Remove "%s%s", add username + message + '\0' */
+ if (buf) {
+ sprintf(buf, format, context->username, plainmsg);
+ if (ops->display_otr_message) {
+ if (!ops->display_otr_message(opdata, accountname,
+ protocol, sender, buf)) {
+ free(*newmessagep);
+ *newmessagep = NULL;
+ edata.ignore_message = 1;
+ }
+ }
+ if (edata.ignore_message != 1) {
+ free(*newmessagep);
+ *newmessagep = buf;
+ edata.ignore_message = 0;
+ } else {
+ free(buf);
+ }
+ }
+ }
+ break;
+
+ case OTRL_MSGTYPE_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 (edata.ignore_message == -1) edata.ignore_message = 1;
+ break;
+ }
+
+ /* If we reassembled a fragmented message, we need to free the
+ * allocated memory now. */
+ if (fragment_assembled) {
+ free(unfragmessage);
+ }
+
+ if (edata.ignore_message == -1) edata.ignore_message = 0;
+ return edata.ignore_message;
+}
+
+/* Send a message to the network, fragmenting first if necessary.
+ * All messages to be sent to the network should go through this
+ * method immediately before they are sent, ie after encryption. */
+gcry_error_t otrl_message_fragment_and_send(const OtrlMessageAppOps *ops,
+ void *opdata, ConnContext *context, const char *message,
+ OtrlFragmentPolicy fragPolicy, char **returnFragment)
+{
+ int mms = 0;
+ if (message && ops->inject_message) {
+ int msglen;
+
+ if (otrl_api_version >= 0x030100 && ops->max_message_size) {
+ mms = ops->max_message_size(opdata, context);
+ }
+ msglen = strlen(message);
+
+ /* Don't incur overhead of fragmentation unless necessary */
+ if(mms != 0 && msglen > mms) {
+ char **fragments;
+ gcry_error_t err;
+ int i;
+ int fragment_count = ((msglen - 1) / (mms -19)) + 1;
+ /* like ceil(msglen/(mms - 19)) */
+
+ err = otrl_proto_fragment_create(mms, fragment_count, &fragments,
+ message);
+ if (err) {
+ return err;
+ }
+
+ /* Determine which fragments to send and which to return
+ * based on given Fragment Policy. If the first fragment
+ * should be returned instead of sent, store it. */
+ if (fragPolicy == OTRL_FRAGMENT_SEND_ALL_BUT_FIRST) {
+ *returnFragment = strdup(fragments[0]);
+ } else {
+ ops->inject_message(opdata, context->accountname,
+ context->protocol, context->username, fragments[0]);
+ }
+ for (i=1; i<fragment_count-1; i++) {
+ ops->inject_message(opdata, context->accountname,
+ context->protocol, context->username, fragments[i]);
+ }
+ /* If the last fragment should be stored instead of sent,
+ * store it */
+ if (fragPolicy == OTRL_FRAGMENT_SEND_ALL_BUT_LAST) {
+ *returnFragment = strdup(fragments[fragment_count-1]);
+ } else {
+ ops->inject_message(opdata, context->accountname,
+ context->protocol, context->username, fragments[fragment_count-1]);
+ }
+ /* Now free all fragment memory */
+ otrl_proto_fragment_free(&fragments, fragment_count);
+
+ } else {
+ /* No fragmentation necessary */
+ if (fragPolicy == OTRL_FRAGMENT_SEND_ALL) {
+ ops->inject_message(opdata, context->accountname,
+ context->protocol, context->username, message);
+ } else {
+ /* Copy and return the entire given message. */
+ int l = strlen(message) + 1;
+ *returnFragment = malloc(sizeof(char)*l);
+ strcpy(*returnFragment, message);
+ }
+ }
+ }
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Put a connection into the PLAINTEXT state, first sending the
+ * other side a notice that we're doing so if we're currently ENCRYPTED,
+ * and we think he's logged in. */
+void otrl_message_disconnect(OtrlUserState us, const 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->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
+ 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,
+ OTRL_MSGFLAGS_IGNORE_UNREADABLE);
+ if (!err) {
+ ops->inject_message(opdata, accountname, protocol,
+ username, encmsg);
+ }
+ free(encmsg);
+ }
+ }
+
+ otrl_context_force_plaintext(context);
+ if (ops->update_context_list) {
+ ops->update_context_list(opdata);
+ }
+}
diff --git a/libotr-3.2.0/src/message.h b/libotr-3.2.0/src/message.h
new file mode 100644
index 0000000..e658e9d
--- /dev/null
+++ b/libotr-3.2.0/src/message.h
@@ -0,0 +1,210 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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 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, or else by using the above notify()
+ * callback. */
+ int (*display_otr_message)(void *opdata, const char *accountname,
+ const char *protocol, const char *username, const char *msg);
+
+ /* 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);
+
+ /* A new fingerprint for the given user has been received. */
+ void (*new_fingerprint)(void *opdata, OtrlUserState us,
+ const char *accountname, const char *protocol,
+ const char *username, unsigned char fingerprint[20]);
+
+ /* 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);
+
+ /* We have completed an authentication, using the D-H keys we
+ * already knew. is_reply indicates whether we initiated the AKE. */
+ 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);
+
+ /* Find the maximum message size supported by this protocol. */
+ int (*max_message_size)(void *opdata, ConnContext *context);
+
+ /* Return a newly allocated string containing a human-friendly
+ * representation for the given account */
+ const char *(*account_name)(void *opdata, const char *account,
+ const char *protocol);
+
+ /* Deallocate a string returned by account_name */
+ void (*account_name_free)(void *opdata, const char *account_name);
+
+} OtrlMessageAppOps;
+
+/* Deallocate a message allocated by other otrl_message_* routines. */
+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,
+ const 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, const 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);
+
+/* Send a message to the network, fragmenting first if necessary.
+ * All messages to be sent to the network should go through this
+ * method immediately before they are sent, ie after encryption. */
+gcry_error_t otrl_message_fragment_and_send(const OtrlMessageAppOps *ops,
+ void *opdata, ConnContext *context, const char *message,
+ OtrlFragmentPolicy fragPolicy, char **returnFragment);
+
+/* Put a connection into the PLAINTEXT state, first sending the
+ * other side a notice that we're doing so if we're currently ENCRYPTED,
+ * and we think he's logged in. */
+void otrl_message_disconnect(OtrlUserState us, const OtrlMessageAppOps *ops,
+ void *opdata, const char *accountname, const char *protocol,
+ const char *username);
+
+/* Initiate the Socialist Millionaires' Protocol */
+void otrl_message_initiate_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
+ void *opdata, ConnContext *context, const unsigned char *secret,
+ size_t secretlen);
+
+/* Initiate the Socialist Millionaires' Protocol and send a prompt
+ * question to the buddy */
+void otrl_message_initiate_smp_q(OtrlUserState us,
+ const OtrlMessageAppOps *ops, void *opdata, ConnContext *context,
+ const char *question, const unsigned char *secret, size_t secretlen);
+
+/* Respond to a buddy initiating the Socialist Millionaires' Protocol */
+void otrl_message_respond_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
+ void *opdata, ConnContext *context, const unsigned char *secret,
+ size_t secretlen);
+
+/* Abort the SMP. Called when an unexpected SMP message breaks the
+ * normal flow. */
+void otrl_message_abort_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
+ void *opdata, ConnContext *context);
+
+#endif
diff --git a/libotr-3.2.0/src/privkey-t.h b/libotr-3.2.0/src/privkey-t.h
new file mode 100644
index 0000000..3421b8b
--- /dev/null
+++ b/libotr-3.2.0/src/privkey-t.h
@@ -0,0 +1,39 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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_T_H__
+#define __PRIVKEY_T_H__
+
+#include <gcrypt.h>
+
+typedef struct s_OtrlPrivKey {
+ struct s_OtrlPrivKey *next;
+ struct s_OtrlPrivKey **tous;
+
+ char *accountname;
+ char *protocol;
+ unsigned short pubkey_type;
+ gcry_sexp_t privkey;
+ unsigned char *pubkey_data;
+ size_t pubkey_datalen;
+} OtrlPrivKey;
+
+#define OTRL_PUBKEY_TYPE_DSA 0x0000
+
+#endif
diff --git a/libotr-3.2.0/src/privkey.c b/libotr-3.2.0/src/privkey.c
new file mode 100644
index 0000000..78a4a18
--- /dev/null
+++ b/libotr-3.2.0/src/privkey.c
@@ -0,0 +1,749 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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 "privkey.h"
+#include "serial.h"
+
+/* Convert a 20-byte hash value to a 45-byte human-readable value */
+void otrl_privkey_hash_to_human(char human[45], const 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];
+ OtrlPrivKey *p = otrl_privkey_find(us, accountname, protocol);
+
+ if (p) {
+ /* Calculate the hash */
+ gcry_md_hash_buffer(GCRY_MD_SHA1, hash, p->pubkey_data,
+ p->pubkey_datalen);
+
+ /* Now convert it to a human-readable format */
+ otrl_privkey_hash_to_human(fingerprint, hash);
+ } else {
+ return NULL;
+ }
+
+ return fingerprint;
+}
+
+/* Calculate a raw hash of our DSA public key. Return it in the passed
+ * fingerprint buffer. Return NULL on error, or a pointer to the given
+ * buffer on success. */
+unsigned char *otrl_privkey_fingerprint_raw(OtrlUserState us,
+ unsigned char hash[20], const char *accountname, const char *protocol)
+{
+ OtrlPrivKey *p = otrl_privkey_find(us, accountname, protocol);
+
+ if (p) {
+ /* Calculate the hash */
+ gcry_md_hash_buffer(GCRY_MD_SHA1, hash, p->pubkey_data,
+ p->pubkey_datalen);
+ } else {
+ return NULL;
+ }
+
+ return hash;
+}
+
+/* Create a public key block from a private key */
+static gcry_error_t make_pubkey(unsigned char **pubbufp, size_t *publenp,
+ gcry_sexp_t privkey)
+{
+ 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);
+}
+
+/* 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;
+ gcry_error_t err;
+
+ /* 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;
+ }
+
+ err = otrl_privkey_read_FILEp(us, privf);
+
+ fclose(privf);
+ return err;
+}
+
+/* Read a sets of private DSA keys from a FILE* into the given
+ * OtrlUserState. The FILE* must be open for reading. */
+gcry_error_t otrl_privkey_read_FILEp(OtrlUserState us, 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;
+
+ if (!privf) return gcry_error(GPG_ERR_NO_ERROR);
+
+ /* Release any old ideas we had about our keys */
+ otrl_privkey_forget_all(us);
+
+ /* Load the data into a buffer */
+ privfd = fileno(privf);
+ if (fstat(privfd, &st)) {
+ err = gcry_error_from_errno(errno);
+ return err;
+ }
+ buf = malloc(st.st_size);
+ if (!buf && st.st_size > 0) {
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+ if (fread(buf, st.st_size, 1, privf) != 1) {
+ err = gcry_error_from_errno(errno);
+ free(buf);
+ return err;
+ }
+
+ 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;
+ OtrlPrivKey *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);
+ gcry_sexp_release(allkeys);
+ 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);
+ gcry_sexp_release(allkeys);
+ 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);
+ gcry_sexp_release(allkeys);
+ 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);
+ gcry_sexp_release(allkeys);
+ 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);
+ gcry_sexp_release(allkeys);
+ return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
+ }
+ proto = malloc(tokenlen + 1);
+ if (!proto) {
+ free(name);
+ gcry_sexp_release(protos);
+ gcry_sexp_release(privs);
+ gcry_sexp_release(allkeys);
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+ memmove(proto, token, tokenlen);
+ proto[tokenlen] = '\0';
+ gcry_sexp_release(protos);
+
+ /* Make a new OtrlPrivKey entry */
+ p = malloc(sizeof(*p));
+ if (!p) {
+ free(name);
+ free(proto);
+ gcry_sexp_release(privs);
+ gcry_sexp_release(allkeys);
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+
+ /* Fill it in and link it up */
+ p->accountname = name;
+ p->protocol = proto;
+ p->pubkey_type = OTRL_PUBKEY_TYPE_DSA;
+ 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 = make_pubkey(&(p->pubkey_data), &(p->pubkey_datalen), p->privkey);
+ if (err) {
+ gcry_sexp_release(allkeys);
+ otrl_privkey_forget(p);
+ return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
+ }
+ }
+ gcry_sexp_release(allkeys);
+
+ 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;
+ FILE *privf;
+#ifndef WIN32
+ mode_t oldmask;
+#endif
+
+#ifndef WIN32
+ oldmask = umask(077);
+#endif
+ privf = fopen(filename, "w+b");
+ if (!privf) {
+#ifndef WIN32
+ umask(oldmask);
+#endif
+ err = gcry_error_from_errno(errno);
+ return err;
+ }
+
+ err = otrl_privkey_generate_FILEp(us, privf, accountname, protocol);
+
+ fclose(privf);
+#ifndef WIN32
+ umask(oldmask);
+#endif
+ return err;
+}
+
+/* Generate a private DSA key for a given account, storing it into a
+ * FILE*, and loading it into the given OtrlUserState. Overwrite any
+ * previously generated keys for that account in that OtrlUserState.
+ * The FILE* must be open for reading and writing. */
+gcry_error_t otrl_privkey_generate_FILEp(OtrlUserState us, FILE *privf,
+ const char *accountname, const char *protocol)
+{
+ gcry_error_t err;
+ gcry_sexp_t key, parms, privkey;
+ static const char *parmstr = "(genkey (dsa (nbits 4:1024)))";
+ OtrlPrivKey *p;
+
+ if (!privf) return gcry_error(GPG_ERR_NO_ERROR);
+
+ /* 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 */
+ 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");
+
+ fseek(privf, 0, SEEK_SET);
+
+ return otrl_privkey_read_FILEp(us, privf);
+}
+
+/* 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)
+{
+ gcry_error_t err;
+ FILE *storef;
+
+ storef = fopen(filename, "rb");
+ if (!storef) {
+ err = gcry_error_from_errno(errno);
+ return err;
+ }
+
+ err = otrl_privkey_read_fingerprints_FILEp(us, storef, add_app_data, data);
+
+ fclose(storef);
+ return err;
+}
+
+/* Read the fingerprint store from a FILE* into the given
+ * OtrlUserState. Use add_app_data to add application data to each
+ * ConnContext so created. The FILE* must be open for reading. */
+gcry_error_t otrl_privkey_read_fingerprints_FILEp(OtrlUserState us,
+ FILE *storef,
+ void (*add_app_data)(void *data, ConnContext *context),
+ void *data)
+{
+ ConnContext *context;
+ char storeline[1000];
+ unsigned char fingerprint[20];
+ size_t maxsize = sizeof(storeline);
+
+ if (!storef) return gcry_error(GPG_ERR_NO_ERROR);
+
+ while(fgets(storeline, maxsize, storef)) {
+ char *username;
+ char *accountname;
+ char *protocol;
+ char *hex;
+ char *trust;
+ char *tab;
+ char *eol;
+ Fingerprint *fng;
+ int i, j;
+ /* Parse the line, which should be of the form:
+ * username\taccountname\tprotocol\t40_hex_nybbles\n */
+ username = storeline;
+ tab = strchr(username, '\t');
+ if (!tab) continue;
+ *tab = '\0';
+
+ accountname = tab + 1;
+ tab = strchr(accountname, '\t');
+ if (!tab) continue;
+ *tab = '\0';
+
+ protocol = tab + 1;
+ tab = strchr(protocol, '\t');
+ if (!tab) continue;
+ *tab = '\0';
+
+ hex = tab + 1;
+ tab = strchr(hex, '\t');
+ if (!tab) {
+ eol = strchr(hex, '\r');
+ if (!eol) eol = strchr(hex, '\n');
+ if (!eol) continue;
+ *eol = '\0';
+ trust = NULL;
+ } else {
+ *tab = '\0';
+ trust = tab + 1;
+ eol = strchr(trust, '\r');
+ if (!eol) eol = strchr(trust, '\n');
+ if (!eol) continue;
+ *eol = '\0';
+ }
+
+ 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 */
+ fng = otrl_context_find_fingerprint(context, fingerprint, 1, NULL);
+ otrl_context_set_trust(fng, trust);
+ }
+
+ 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)
+{
+ gcry_error_t err;
+ FILE *storef;
+
+ storef = fopen(filename, "wb");
+ if (!storef) {
+ err = gcry_error_from_errno(errno);
+ return err;
+ }
+
+ err = otrl_privkey_write_fingerprints_FILEp(us, storef);
+
+ fclose(storef);
+ return err;
+}
+
+/* Write the fingerprint store from a given OtrlUserState to a FILE*.
+ * The FILE* must be open for writing. */
+gcry_error_t otrl_privkey_write_fingerprints_FILEp(OtrlUserState us,
+ FILE *storef)
+{
+ ConnContext *context;
+ Fingerprint *fprint;
+
+ if (!storef) return gcry_error(GPG_ERR_NO_ERROR);
+
+ 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, "\t%s\n", fprint->trust ? fprint->trust : "");
+ }
+ }
+
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Fetch the private key from the given OtrlUserState associated with
+ * the given account */
+OtrlPrivKey *otrl_privkey_find(OtrlUserState us, const char *accountname,
+ const char *protocol)
+{
+ OtrlPrivKey *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(OtrlPrivKey *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);
+ }
+}
+
+/* Sign data using a private key. The data must be small enough to be
+ * signed (i.e. already hashed, if necessary). The signature will be
+ * returned in *sigp, which the caller must free(). Its length will be
+ * returned in *siglenp. */
+gcry_error_t otrl_privkey_sign(unsigned char **sigp, size_t *siglenp,
+ OtrlPrivKey *privkey, const unsigned char *data, size_t len)
+{
+ gcry_mpi_t r,s, datampi;
+ gcry_sexp_t dsas, rs, ss, sigs, datas;
+ size_t nr, ns;
+ const enum gcry_mpi_format format = GCRYMPI_FMT_USG;
+
+ if (privkey->pubkey_type != OTRL_PUBKEY_TYPE_DSA)
+ return gcry_error(GPG_ERR_INV_VALUE);
+
+ *sigp = malloc(40);
+ if (sigp == NULL) return gcry_error(GPG_ERR_ENOMEM);
+ *siglenp = 40;
+
+ if (len) {
+ gcry_mpi_scan(&datampi, GCRYMPI_FMT_USG, data, len, NULL);
+ } else {
+ datampi = gcry_mpi_set_ui(NULL, 0);
+ }
+ gcry_sexp_build(&datas, NULL, "(%m)", datampi);
+ gcry_mpi_release(datampi);
+ gcry_pk_sign(&sigs, datas, privkey->privkey);
+ gcry_sexp_release(datas);
+ 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(*sigp, 0, 40);
+ gcry_mpi_print(format, (*sigp)+(20-nr), nr, NULL, r);
+ gcry_mpi_print(format, (*sigp)+20+(20-ns), ns, NULL, s);
+ gcry_mpi_release(r);
+ gcry_mpi_release(s);
+
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Verify a signature on data using a public key. The data must be
+ * small enough to be signed (i.e. already hashed, if necessary). */
+gcry_error_t otrl_privkey_verify(const unsigned char *sigbuf, size_t siglen,
+ unsigned short pubkey_type, gcry_sexp_t pubs,
+ const unsigned char *data, size_t len)
+{
+ gcry_error_t err;
+ gcry_mpi_t datampi,r,s;
+ gcry_sexp_t datas, sigs;
+
+ if (pubkey_type != OTRL_PUBKEY_TYPE_DSA || siglen != 40)
+ return gcry_error(GPG_ERR_INV_VALUE);
+
+ if (len) {
+ gcry_mpi_scan(&datampi, GCRYMPI_FMT_USG, data, len, NULL);
+ } else {
+ datampi = gcry_mpi_set_ui(NULL, 0);
+ }
+ gcry_sexp_build(&datas, NULL, "(%m)", datampi);
+ gcry_mpi_release(datampi);
+ gcry_mpi_scan(&r, GCRYMPI_FMT_USG, sigbuf, 20, NULL);
+ gcry_mpi_scan(&s, GCRYMPI_FMT_USG, sigbuf+20, 20, NULL);
+ gcry_sexp_build(&sigs, NULL, "(sig-val (dsa (r %m)(s %m)))", r, s);
+ gcry_mpi_release(r);
+ gcry_mpi_release(s);
+
+ err = gcry_pk_verify(sigs, datas, pubs);
+ gcry_sexp_release(datas);
+ gcry_sexp_release(sigs);
+
+ return err;
+}
+gcry_error_t otrl_account_write(FILE *privf, const char *accountname,
+ const char *protocol, gcry_sexp_t privkey) {
+ return account_write(privf, accountname, protocol, privkey);
+} \ No newline at end of file
diff --git a/libotr-3.2.0/src/privkey.h b/libotr-3.2.0/src/privkey.h
new file mode 100644
index 0000000..1208f54
--- /dev/null
+++ b/libotr-3.2.0/src/privkey.h
@@ -0,0 +1,115 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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 <stdio.h>
+#include "privkey-t.h"
+#include "userstate.h"
+
+gcry_error_t otrl_account_write(FILE *privf, const char *accountname,
+ const char *protocol, gcry_sexp_t privkey);
+
+/* Convert a 20-byte hash value to a 45-byte human-readable value */
+void otrl_privkey_hash_to_human(char human[45], const 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);
+
+/* Calculate a raw hash of our DSA public key. Return it in the passed
+ * fingerprint buffer. Return NULL on error, or a pointer to the given
+ * buffer on success. */
+unsigned char *otrl_privkey_fingerprint_raw(OtrlUserState us,
+ unsigned char hash[20], const char *accountname, const char *protocol);
+
+/* Read a sets of private DSA keys from a file on disk into the given
+ * OtrlUserState. */
+gcry_error_t otrl_privkey_read(OtrlUserState us, const char *filename);
+
+/* Read a sets of private DSA keys from a FILE* into the given
+ * OtrlUserState. The FILE* must be open for reading. */
+gcry_error_t otrl_privkey_read_FILEp(OtrlUserState us, FILE *privf);
+
+/* 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);
+
+/* Generate a private DSA key for a given account, storing it into a
+ * FILE*, and loading it into the given OtrlUserState. Overwrite any
+ * previously generated keys for that account in that OtrlUserState.
+ * The FILE* must be open for reading and writing. */
+gcry_error_t otrl_privkey_generate_FILEp(OtrlUserState us, FILE *privf,
+ 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);
+
+/* Read the fingerprint store from a FILE* into the given
+ * OtrlUserState. Use add_app_data to add application data to each
+ * ConnContext so created. The FILE* must be open for reading. */
+gcry_error_t otrl_privkey_read_fingerprints_FILEp(OtrlUserState us,
+ FILE *storef,
+ 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);
+
+/* Write the fingerprint store from a given OtrlUserState to a FILE*.
+ * The FILE* must be open for writing. */
+gcry_error_t otrl_privkey_write_fingerprints_FILEp(OtrlUserState us,
+ FILE *storef);
+
+/* Fetch the private key from the given OtrlUserState associated with
+ * the given account */
+OtrlPrivKey *otrl_privkey_find(OtrlUserState us, const char *accountname,
+ const char *protocol);
+
+/* Forget a private key */
+void otrl_privkey_forget(OtrlPrivKey *privkey);
+
+/* Forget all private keys in a given OtrlUserState. */
+void otrl_privkey_forget_all(OtrlUserState us);
+
+/* Sign data using a private key. The data must be small enough to be
+ * signed (i.e. already hashed, if necessary). The signature will be
+ * returned in *sigp, which the caller must free(). Its length will be
+ * returned in *siglenp. */
+gcry_error_t otrl_privkey_sign(unsigned char **sigp, size_t *siglenp,
+ OtrlPrivKey *privkey, const unsigned char *data, size_t len);
+
+/* Verify a signature on data using a public key. The data must be
+ * small enough to be signed (i.e. already hashed, if necessary). */
+gcry_error_t otrl_privkey_verify(const unsigned char *sigbuf, size_t siglen,
+ unsigned short pubkey_type, gcry_sexp_t pubs,
+ const unsigned char *data, size_t len);
+
+#endif
diff --git a/libotr-3.2.0/src/proto.c b/libotr-3.2.0/src/proto.c
new file mode 100644
index 0000000..9f76455
--- /dev/null
+++ b/libotr-3.2.0/src/proto.c
@@ -0,0 +1,906 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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"
+#include "serial.h"
+
+/* For now, we need to know the API version the client is using so that
+ * we don't use any UI callbacks it hasn't set. */
+unsigned int otrl_api_version = 0;
+
+/* Initialize the OTR library. Pass the version of the API you are
+ * using. */
+void otrl_init(unsigned int ver_major, unsigned int ver_minor,
+ unsigned int ver_sub)
+{
+ unsigned int api_version;
+
+ /* The major versions have to match, and you can't be using a newer
+ * minor version than we expect. */
+ if (ver_major != OTRL_VERSION_MAJOR || ver_minor > OTRL_VERSION_MINOR) {
+ 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);
+ }
+
+ /* Set the API version. If we get called multiple times for some
+ * reason, take the smallest value. */
+ api_version = (ver_major << 16) | (ver_minor << 8) | (ver_sub);
+ if (otrl_api_version == 0 || otrl_api_version > api_version) {
+ otrl_api_version = api_version;
+ }
+
+ /* Initialize the memory module */
+ otrl_mem_init();
+
+ /* Initialize the DH module */
+ otrl_dh_init();
+
+ /* Initialize the SM module */
+ otrl_sm_init();
+}
+
+/* Return a pointer to a static string containing the version number of
+ * the OTR library. */
+const char *otrl_version(void)
+{
+ return OTRL_VERSION;
+}
+
+/* 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;
+ } else {
+ otrl_dh_session_blank(&(context->sesskeys[0][0]));
+ }
+ 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;
+ } else {
+ otrl_dh_session_blank(&(context->sesskeys[0][1]));
+ }
+ 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, OtrlPolicy policy)
+{
+ char *msg;
+ int v1_supported, v2_supported;
+ const char *version_tag;
+ /* 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%s\n<b>%s</b> has requested an "
+ "<a href=\"http://otr.cypherpunks.ca/\">Off-the-Record "
+ "private conversation</a>. However, you do not have a plugin "
+ "to support that.\nSee <a href=\"http://otr.cypherpunks.ca/\">"
+ "http://otr.cypherpunks.ca/</a> for more information.";
+
+ /* Figure out the version tag */
+ v1_supported = (policy & OTRL_POLICY_ALLOW_V1);
+ v2_supported = (policy & OTRL_POLICY_ALLOW_V2);
+ if (v1_supported) {
+ if (v2_supported) {
+ version_tag = "?v2?";
+ } else {
+ version_tag = "?";
+ }
+ } else {
+ if (v2_supported) {
+ version_tag = "v2?";
+ } else {
+ version_tag = "v?";
+ }
+ }
+
+ /* Remove two "%s", add '\0' */
+ msg = malloc(strlen(format) + strlen(version_tag) + strlen(ourname) - 3);
+ if (!msg) return NULL;
+ sprintf(msg, format, version_tag, ourname);
+ return msg;
+}
+
+/* Return the best version of OTR support by both sides, given an OTR
+ * Query Message and the local policy. */
+unsigned int otrl_proto_query_bestversion(const char *querymsg,
+ OtrlPolicy policy)
+{
+ char *otrtag;
+ unsigned int query_versions = 0;
+
+ otrtag = strstr(querymsg, "?OTR");
+ otrtag += 4;
+ if (*otrtag == '?') {
+ query_versions = (1<<0);
+ ++otrtag;
+ }
+ if (*otrtag == 'v') {
+ for(++otrtag; *otrtag && *otrtag != '?'; ++otrtag) {
+ switch(*otrtag) {
+ case '2':
+ query_versions |= (1<<1);
+ break;
+ }
+ }
+ }
+
+ if ((policy & OTRL_POLICY_ALLOW_V2) && (query_versions & (1<<1))) {
+ return 2;
+ }
+ if ((policy & OTRL_POLICY_ALLOW_V1) && (query_versions & (1<<0))) {
+ return 1;
+ }
+ return 0;
+}
+
+/* Locate any whitespace tag in this message, and return the best
+ * version of OTR support on both sides. Set *starttagp and *endtagp to
+ * the start and end of the located tag, so that it can be snipped out. */
+unsigned int otrl_proto_whitespace_bestversion(const char *msg,
+ const char **starttagp, const char **endtagp, OtrlPolicy policy)
+{
+ const char *starttag, *endtag;
+ unsigned int query_versions = 0;
+
+ *starttagp = NULL;
+ *endtagp = NULL;
+
+ starttag = strstr(msg, OTRL_MESSAGE_TAG_BASE);
+ if (!starttag) return 0;
+
+ endtag = starttag + strlen(OTRL_MESSAGE_TAG_BASE);
+
+ /* Look for groups of 8 spaces and/or tabs */
+ while(1) {
+ int i;
+ int allwhite = 1;
+ for(i=0;i<8;++i) {
+ if (endtag[i] != ' ' && endtag[i] != '\t') {
+ allwhite = 0;
+ break;
+ }
+ }
+ if (allwhite) {
+ if (!strncmp(endtag, OTRL_MESSAGE_TAG_V1, 8)) {
+ query_versions |= (1<<0);
+ }
+ if (!strncmp(endtag, OTRL_MESSAGE_TAG_V2, 8)) {
+ query_versions |= (1<<1);
+ }
+ endtag += 8;
+ } else {
+ break;
+ }
+ }
+
+ *starttagp = starttag;
+ *endtagp = endtag;
+
+ if ((policy & OTRL_POLICY_ALLOW_V2) && (query_versions & (1<<1))) {
+ return 2;
+ }
+ if ((policy & OTRL_POLICY_ALLOW_V1) && (query_versions & (1<<0))) {
+ return 1;
+ }
+ return 0;
+}
+
+/* Return the Message type of the given message. */
+OtrlMessageType otrl_proto_message_type(const char *message)
+{
+ char *otrtag;
+
+ otrtag = strstr(message, "?OTR");
+
+ if (!otrtag) {
+ if (strstr(message, OTRL_MESSAGE_TAG_BASE)) {
+ return OTRL_MSGTYPE_TAGGEDPLAINTEXT;
+ } else {
+ return OTRL_MSGTYPE_NOTOTR;
+ }
+ }
+
+ if (!strncmp(otrtag, "?OTR?", 5)) return OTRL_MSGTYPE_QUERY;
+ if (!strncmp(otrtag, "?OTRv", 5)) return OTRL_MSGTYPE_QUERY;
+ if (!strncmp(otrtag, "?OTR:AAIC", 9)) return OTRL_MSGTYPE_DH_COMMIT;
+ if (!strncmp(otrtag, "?OTR:AAIK", 9)) return OTRL_MSGTYPE_DH_KEY;
+ if (!strncmp(otrtag, "?OTR:AAIR", 9)) return OTRL_MSGTYPE_REVEALSIG;
+ if (!strncmp(otrtag, "?OTR:AAIS", 9)) return OTRL_MSGTYPE_SIGNATURE;
+ if (!strncmp(otrtag, "?OTR:AAEK", 9)) return OTRL_MSGTYPE_V1_KEYEXCH;
+ if (!strncmp(otrtag, "?OTR:AAED", 9)) return OTRL_MSGTYPE_DATA;
+ if (!strncmp(otrtag, "?OTR:AAID", 9)) return OTRL_MSGTYPE_DATA;
+ if (!strncmp(otrtag, "?OTR Error:", 11)) return OTRL_MSGTYPE_ERROR;
+
+ return OTRL_MSGTYPE_UNKNOWN;
+}
+
+/* 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, const OtrlTLV *tlvs, unsigned char flags)
+{
+ 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;
+ unsigned char *msgbuf = NULL;
+ enum gcry_mpi_format format = GCRYMPI_FMT_USG;
+ char *msgdup;
+ int version = context->protocol_version;
+
+ /* Make sure we're actually supposed to be able to encrypt */
+ if (context->msgstate != OTRL_MSGSTATE_ENCRYPTED ||
+ 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 + (version == 2 ? 1 : 0) + 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;
+ if (version == 1) {
+ memmove(bufp, "\x00\x01\x03", 3); /* header */
+ } else {
+ memmove(bufp, "\x00\x02\x03", 3); /* header */
+ }
+ debug_data("Header", bufp, 3);
+ bufp += 3; lenp -= 3;
+ if (version == 2) {
+ bufp[0] = flags;
+ bufp += 1; lenp -= 1;
+ }
+ 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 = gcry_error(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;
+}
+
+/* Extract the flags from an otherwise unreadable Data Message. */
+gcry_error_t otrl_proto_data_read_flags(const char *datamsg,
+ unsigned char *flagsp)
+{
+ char *otrtag, *endtag;
+ unsigned char *rawmsg = NULL;
+ unsigned char *bufp;
+ size_t msglen, rawlen, lenp;
+ unsigned char version;
+
+ if (flagsp) *flagsp = 0;
+ 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) {
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+ rawlen = otrl_base64_decode(rawmsg, otrtag+5, msglen-5); /* actual size */
+
+ bufp = rawmsg;
+ lenp = rawlen;
+
+ require_len(3);
+ if (memcmp(bufp, "\x00\x01\x03", 3) && memcmp(bufp, "\x00\x02\x03", 3)) {
+ /* Invalid header */
+ goto invval;
+ }
+ version = bufp[1];
+ bufp += 3; lenp -= 3;
+
+ if (version == 2) {
+ require_len(1);
+ if (flagsp) *flagsp = bufp[0];
+ bufp += 1; lenp -= 1;
+ }
+
+ free(rawmsg);
+ return gcry_error(GPG_ERR_NO_ERROR);
+
+invval:
+ free(rawmsg);
+ return gcry_error(GPG_ERR_INV_VALUE);
+}
+
+/* Accept an OTR Data Message in datamsg. Decrypt it and put the
+ * plaintext into *plaintextp, and any TLVs into tlvsp. Put any
+ * received flags into *flagsp (if non-NULL). */
+gcry_error_t otrl_proto_accept_data(char **plaintextp, OtrlTLV **tlvsp,
+ ConnContext *context, const char *datamsg, unsigned char *flagsp)
+{
+ 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;
+ unsigned char version;
+
+ *plaintextp = NULL;
+ *tlvsp = NULL;
+ if (flagsp) *flagsp = 0;
+ 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) && memcmp(bufp, "\x00\x02\x03", 3)) {
+ /* Invalid header */
+ goto invval;
+ }
+ version = bufp[1];
+ bufp += 3; lenp -= 3;
+
+ if (version == 2) {
+ require_len(1);
+ if (flagsp) *flagsp = bufp[0];
+ bufp += 1; lenp -= 1;
+ }
+ 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 = (char *)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;
+}
+
+/* Accumulate a potential fragment into the current context. */
+OtrlFragmentResult otrl_proto_fragment_accumulate(char **unfragmessagep,
+ ConnContext *context, const char *msg)
+{
+ OtrlFragmentResult res = OTRL_FRAGMENT_INCOMPLETE;
+ const char *tag;
+
+ tag = strstr(msg, "?OTR,");
+ if (tag) {
+ unsigned short n = 0, k = 0;
+ int start = 0, end = 0;
+
+ sscanf(tag, "?OTR,%hu,%hu,%n%*[^,],%n", &k, &n, &start, &end);
+ if (k > 0 && n > 0 && k <= n && start > 0 && end > 0 && start < end) {
+ if (k == 1) {
+ int fraglen = end - start - 1;
+ free(context->fragment);
+ context->fragment = malloc(fraglen + 1);
+ if (fraglen + 1 > fraglen && context->fragment) {
+ memmove(context->fragment, tag + start, fraglen);
+ context->fragment_len = fraglen;
+ context->fragment[context->fragment_len] = '\0';
+ context->fragment_n = n;
+ context->fragment_k = k;
+ } else {
+ free(context->fragment);
+ context->fragment = NULL;
+ context->fragment_len = 0;
+ context->fragment_n = 0;
+ context->fragment_k = 0;
+ }
+ } else if (n == context->fragment_n &&
+ k == context->fragment_k + 1) {
+ int fraglen = end - start - 1;
+ char *newfrag = realloc(context->fragment,
+ context->fragment_len + fraglen + 1);
+ if (context->fragment_len + fraglen + 1 > fraglen && newfrag) {
+ context->fragment = newfrag;
+ memmove(context->fragment + context->fragment_len,
+ tag + start, fraglen);
+ context->fragment_len += fraglen;
+ context->fragment[context->fragment_len] = '\0';
+ context->fragment_k = k;
+ } else {
+ free(context->fragment);
+ context->fragment = NULL;
+ context->fragment_len = 0;
+ context->fragment_n = 0;
+ context->fragment_k = 0;
+ }
+ } else {
+ free(context->fragment);
+ context->fragment = NULL;
+ context->fragment_len = 0;
+ context->fragment_n = 0;
+ context->fragment_k = 0;
+ }
+ }
+
+ if (context->fragment_n > 0 &&
+ context->fragment_n == context->fragment_k) {
+ /* We've got a complete message */
+ *unfragmessagep = context->fragment;
+ context->fragment = NULL;
+ context->fragment_len = 0;
+ context->fragment_n = 0;
+ context->fragment_k = 0;
+ res = OTRL_FRAGMENT_COMPLETE;
+ }
+ } else {
+ /* Unfragmented message, so discard any fragment we may have */
+ free(context->fragment);
+ context->fragment = NULL;
+ context->fragment_len = 0;
+ context->fragment_n = 0;
+ context->fragment_k = 0;
+ res = OTRL_FRAGMENT_UNFRAGMENTED;
+ }
+
+ return res;
+}
+
+/* Create a fragmented message. */
+gcry_error_t otrl_proto_fragment_create(int mms, int fragment_count,
+ char ***fragments, const char *message)
+{
+ char *fragdata;
+ int fragdatalen = 0;
+ unsigned short curfrag = 0;
+ int index = 0;
+ int msglen = strlen(message);
+ int headerlen = 19; /* Should vary by number of msgs */
+
+ char **fragmentarray = malloc(fragment_count * sizeof(char*));
+ if(!fragmentarray) return gcry_error(GPG_ERR_ENOMEM);
+
+ /*
+ * Find the next message fragment and store it in the array.
+ */
+ for(curfrag = 1; curfrag <= fragment_count; curfrag++) {
+ int i;
+ char *fragmentmsg;
+
+ if (msglen - index < mms - headerlen) {
+ fragdatalen = msglen - index;
+ } else {
+ fragdatalen = mms - headerlen;
+ }
+ fragdata = malloc(fragdatalen + 1);
+ if(!fragdata) {
+ for (i=0; i<curfrag-1; free(fragmentarray[i++])) {}
+ free(fragmentarray);
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+ strncpy(fragdata, message, fragdatalen);
+ fragdata[fragdatalen] = 0;
+
+ fragmentmsg = malloc(fragdatalen+headerlen+1);
+ if(!fragmentmsg) {
+ for (i=0; i<curfrag-1; free(fragmentarray[i++])) {}
+ free(fragmentarray);
+ free(fragdata);
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+
+ /*
+ * Create the actual fragment and store it in the array
+ */
+ _snprintf(fragmentmsg, fragdatalen + headerlen, "?OTR,%05hu,%05hu,%s,", curfrag, fragment_count, fragdata);
+ fragmentmsg[fragdatalen + headerlen] = 0;
+
+ fragmentarray[curfrag-1] = fragmentmsg;
+
+ free(fragdata);
+ index += fragdatalen;
+ message += fragdatalen;
+ }
+
+ *fragments = fragmentarray;
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Free a string array containing fragment messages. */
+void otrl_proto_fragment_free(char ***fragments, unsigned short arraylen)
+{
+ int i;
+ char **fragmentarray = *fragments;
+ if(fragmentarray) {
+ for(i = 0; i < arraylen; i++)
+ {
+ if(fragmentarray[i]) {
+ free(fragmentarray[i]);
+ }
+ }
+ free(fragmentarray);
+ }
+}
+
diff --git a/libotr-3.2.0/src/proto.h b/libotr-3.2.0/src/proto.h
new file mode 100644
index 0000000..d7b0ae6
--- /dev/null
+++ b/libotr-3.2.0/src/proto.h
@@ -0,0 +1,150 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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 OTRL_MESSAGE_TAG_BASE " \t \t\t\t\t \t \t \t "
+/* The following must each be of length 8 */
+#define OTRL_MESSAGE_TAG_V1 " \t \t \t "
+#define OTRL_MESSAGE_TAG_V2 " \t\t \t "
+
+/* The possible flags contained in a Data Message */
+#define OTRL_MSGFLAGS_IGNORE_UNREADABLE 0x01
+
+typedef unsigned int OtrlPolicy;
+
+#define OTRL_POLICY_ALLOW_V1 0x01
+#define OTRL_POLICY_ALLOW_V2 0x02
+#define OTRL_POLICY_REQUIRE_ENCRYPTION 0x04
+#define OTRL_POLICY_SEND_WHITESPACE_TAG 0x08
+#define OTRL_POLICY_WHITESPACE_START_AKE 0x10
+#define OTRL_POLICY_ERROR_START_AKE 0x20
+
+#define OTRL_POLICY_VERSION_MASK (OTRL_POLICY_ALLOW_V1 | OTRL_POLICY_ALLOW_V2)
+
+/* For v1 compatibility */
+#define OTRL_POLICY_NEVER 0x00
+#define OTRL_POLICY_OPPORTUNISTIC \
+ ( OTRL_POLICY_ALLOW_V1 | \
+ OTRL_POLICY_ALLOW_V2 | \
+ OTRL_POLICY_SEND_WHITESPACE_TAG | \
+ OTRL_POLICY_WHITESPACE_START_AKE | \
+ OTRL_POLICY_ERROR_START_AKE )
+#define OTRL_POLICY_MANUAL \
+ ( OTRL_POLICY_ALLOW_V1 | \
+ OTRL_POLICY_ALLOW_V2 )
+#define OTRL_POLICY_ALWAYS \
+ ( OTRL_POLICY_ALLOW_V1 | \
+ OTRL_POLICY_ALLOW_V2 | \
+ OTRL_POLICY_REQUIRE_ENCRYPTION | \
+ OTRL_POLICY_WHITESPACE_START_AKE | \
+ OTRL_POLICY_ERROR_START_AKE )
+#define OTRL_POLICY_DEFAULT OTRL_POLICY_OPPORTUNISTIC
+
+typedef enum {
+ OTRL_MSGTYPE_NOTOTR,
+ OTRL_MSGTYPE_TAGGEDPLAINTEXT,
+ OTRL_MSGTYPE_QUERY,
+ OTRL_MSGTYPE_DH_COMMIT,
+ OTRL_MSGTYPE_DH_KEY,
+ OTRL_MSGTYPE_REVEALSIG,
+ OTRL_MSGTYPE_SIGNATURE,
+ OTRL_MSGTYPE_V1_KEYEXCH,
+ OTRL_MSGTYPE_DATA,
+ OTRL_MSGTYPE_ERROR,
+ OTRL_MSGTYPE_UNKNOWN
+} OtrlMessageType;
+
+typedef enum {
+ OTRL_FRAGMENT_UNFRAGMENTED,
+ OTRL_FRAGMENT_INCOMPLETE,
+ OTRL_FRAGMENT_COMPLETE
+} OtrlFragmentResult;
+
+typedef enum {
+ OTRL_FRAGMENT_SEND_ALL,
+ OTRL_FRAGMENT_SEND_ALL_BUT_FIRST,
+ OTRL_FRAGMENT_SEND_ALL_BUT_LAST
+} OtrlFragmentPolicy;
+
+/* Initialize the OTR library. Pass the version of the API you are
+ * using. */
+void otrl_init(unsigned int ver_major, unsigned int ver_minor,
+ 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);
+
+/* 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, OtrlPolicy policy);
+
+/* Return the best version of OTR support by both sides, given an OTR
+ * Query Message and the local policy. */
+unsigned int otrl_proto_query_bestversion(const char *querymsg,
+ OtrlPolicy policy);
+
+/* Locate any whitespace tag in this message, and return the best
+ * version of OTR support on both sides. Set *starttagp and *endtagp to
+ * the start and end of the located tag, so that it can be snipped out. */
+unsigned int otrl_proto_whitespace_bestversion(const char *msg,
+ const char **starttagp, const char **endtagp, OtrlPolicy policy);
+
+/* Return the Message type of the given message. */
+OtrlMessageType otrl_proto_message_type(const char *message);
+
+/* 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, const OtrlTLV *tlvs, unsigned char flags);
+
+/* Extract the flags from an otherwise unreadable Data Message. */
+gcry_error_t otrl_proto_data_read_flags(const char *datamsg,
+ unsigned char *flagsp);
+
+/* Accept an OTR Data Message in datamsg. Decrypt it and put the
+ * plaintext into *plaintextp, and any TLVs into tlvsp. Put any
+ * received flags into *flagsp (if non-NULL). */
+gcry_error_t otrl_proto_accept_data(char **plaintextp, OtrlTLV **tlvsp,
+ ConnContext *context, const char *datamsg, unsigned char *flagsp);
+
+/* Accumulate a potential fragment into the current context. */
+OtrlFragmentResult otrl_proto_fragment_accumulate(char **unfragmessagep,
+ ConnContext *context, const char *msg);
+
+gcry_error_t otrl_proto_fragment_create(int mms, int fragment_count,
+ char ***fragments, const char *message);
+
+void otrl_proto_fragment_free(char ***fragments, unsigned short arraylen);
+#endif
diff --git a/libotr-3.2.0/src/serial.h b/libotr-3.2.0/src/serial.h
new file mode 100644
index 0000000..edc3184
--- /dev/null
+++ b/libotr-3.2.0/src/serial.h
@@ -0,0 +1,85 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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 __SERIAL_H__
+#define __SERIAL_H__
+
+#undef DEBUG
+
+#ifdef DEBUG
+
+#include <stdio.h>
+
+#define debug_data(t,b,l) do { const unsigned char *data = (b); size_t i; \
+ fprintf(stderr, "%s: ", (t)); \
+ for(i=0;i<(l);++i) { \
+ fprintf(stderr, "%02x", data[i]); \
+ } \
+ fprintf(stderr, "\n"); \
+ } while(0)
+
+#define debug_int(t,b) do { const unsigned char *data = (b); \
+ unsigned int v = \
+ (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; \
+ fprintf(stderr, "%s: %u (0x%x)\n", (t), v, v); \
+ } while(0)
+
+#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); \
+ if (mpilen) { \
+ require_len(mpilen); \
+ gcry_mpi_scan(&(x), GCRYMPI_FMT_USG, bufp, mpilen, NULL); \
+ } else { \
+ (x) = gcry_mpi_set_ui(NULL, 0); \
+ } \
+ bufp += mpilen; lenp -= mpilen; \
+ } while(0)
+
+#endif
diff --git a/libotr-3.2.0/src/sm.c b/libotr-3.2.0/src/sm.c
new file mode 100644
index 0000000..535ae22
--- /dev/null
+++ b/libotr-3.2.0/src/sm.c
@@ -0,0 +1,929 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@cypherpunks.ca>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General
+ * Public License as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* system headers */
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+/* libgcrypt headers */
+#include <gcrypt.h>
+
+/* libotr headers */
+#include "sm.h"
+#include "serial.h"
+
+static const int SM_MSG1_LEN = 6;
+static const int SM_MSG2_LEN = 11;
+static const int SM_MSG3_LEN = 8;
+static const int SM_MSG4_LEN = 3;
+
+/* The modulus p */
+static const char* SM_MODULUS_S = "0x"
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+ "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF";
+/* The order of the group q = (p-1)/2 */
+static const char* SM_ORDER_S = "0x"
+ "7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68"
+ "948127044533E63A0105DF531D89CD9128A5043CC71A026E"
+ "F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122"
+ "F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6"
+ "F71C35FDAD44CFD2D74F9208BE258FF324943328F6722D9E"
+ "E1003E5C50B1DF82CC6D241B0E2AE9CD348B1FD47E9267AF"
+ "C1B2AE91EE51D6CB0E3179AB1042A95DCF6A9483B84B4B36"
+ "B3861AA7255E4C0278BA36046511B993FFFFFFFFFFFFFFFF";
+static const char *SM_GENERATOR_S = "0x02";
+static const int SM_MOD_LEN_BITS = 1536;
+static const int SM_MOD_LEN_BYTES = 192;
+
+static gcry_mpi_t SM_MODULUS = NULL;
+static gcry_mpi_t SM_GENERATOR = NULL;
+static gcry_mpi_t SM_ORDER = NULL;
+static gcry_mpi_t SM_MODULUS_MINUS_2 = NULL;
+
+/*
+ * Call this once, at plugin load time. It sets up the modulus and
+ * generator MPIs.
+ */
+void otrl_sm_init(void)
+{
+ gcry_check_version(NULL);
+ gcry_mpi_scan(&SM_MODULUS, GCRYMPI_FMT_HEX, SM_MODULUS_S, 0, NULL);
+ gcry_mpi_scan(&SM_ORDER, GCRYMPI_FMT_HEX, SM_ORDER_S, 0, NULL);
+ gcry_mpi_scan(&SM_GENERATOR, GCRYMPI_FMT_HEX, SM_GENERATOR_S,
+ 0, NULL);
+ SM_MODULUS_MINUS_2 = gcry_mpi_new(SM_MOD_LEN_BITS);
+ gcry_mpi_sub_ui(SM_MODULUS_MINUS_2, SM_MODULUS, 2);
+}
+
+/*
+ * Initialize the fields of a SM state.
+ */
+void otrl_sm_state_new(OtrlSMState *smst)
+{
+ smst->secret = NULL;
+ smst->x2 = NULL;
+ smst->x3 = NULL;
+ smst->g1 = NULL;
+ smst->g2 = NULL;
+ smst->g3 = NULL;
+ smst->g3o = NULL;
+ smst->p = NULL;
+ smst->q = NULL;
+ smst->pab = NULL;
+ smst->qab = NULL;
+ smst->nextExpected = OTRL_SMP_EXPECT1;
+ smst->received_question = 0;
+ smst->sm_prog_state = OTRL_SMP_PROG_OK;
+}
+
+/*
+ * Initialize the fields of a SM state. Called the first time that
+ * a user begins an SMP session.
+ */
+void otrl_sm_state_init(OtrlSMState *smst)
+{
+ otrl_sm_state_free(smst);
+ smst->secret = gcry_mpi_new(SM_MOD_LEN_BITS);
+ smst->x2 = NULL;
+ smst->x3 = NULL;
+ smst->g1 = gcry_mpi_copy(SM_GENERATOR);
+ smst->g2 = gcry_mpi_new(SM_MOD_LEN_BITS);
+ smst->g3 = gcry_mpi_new(SM_MOD_LEN_BITS);
+ smst->g3o = gcry_mpi_new(SM_MOD_LEN_BITS);
+ smst->p = gcry_mpi_new(SM_MOD_LEN_BITS);
+ smst->q = gcry_mpi_new(SM_MOD_LEN_BITS);
+ smst->pab = gcry_mpi_new(SM_MOD_LEN_BITS);
+ smst->qab = gcry_mpi_new(SM_MOD_LEN_BITS);
+ smst->received_question = 0;
+ smst->sm_prog_state = OTRL_SMP_PROG_OK;
+}
+
+/*
+ * Initialize the fields of a SM message1.
+ * [0] = g2a, [1] = c2, [2] = d2, [3] = g3a, [4] = c3, [5] = d3
+ */
+void otrl_sm_msg1_init(gcry_mpi_t **msg1)
+{
+ gcry_mpi_t *msg = malloc(SM_MSG1_LEN * sizeof(gcry_mpi_t));
+ msg[0] = gcry_mpi_new(SM_MOD_LEN_BITS);
+ msg[1] = NULL;
+ msg[2] = gcry_mpi_new(SM_MOD_LEN_BITS);
+ msg[3] = gcry_mpi_new(SM_MOD_LEN_BITS);
+ msg[4] = NULL;
+ msg[5] = gcry_mpi_new(SM_MOD_LEN_BITS);
+
+ *msg1 = msg;
+}
+
+/*
+ * Initialize the fields of a SM message2.
+ * [0] = g2b, [1] = c2, [2] = d2, [3] = g3b, [4] = c3, [5] = d3
+ * [6] = pb, [7] = qb, [8] = cp, [9] = d5, [10] = d6
+ */
+void otrl_sm_msg2_init(gcry_mpi_t **msg2)
+{
+ gcry_mpi_t *msg = malloc(SM_MSG2_LEN * sizeof(gcry_mpi_t));
+ msg[0] = gcry_mpi_new(SM_MOD_LEN_BITS);
+ msg[1] = NULL;
+ msg[2] = gcry_mpi_new(SM_MOD_LEN_BITS);
+ msg[3] = gcry_mpi_new(SM_MOD_LEN_BITS);
+ msg[4] = NULL;
+ msg[5] = gcry_mpi_new(SM_MOD_LEN_BITS);
+ msg[6] = gcry_mpi_new(SM_MOD_LEN_BITS);
+ msg[7] = gcry_mpi_new(SM_MOD_LEN_BITS);
+ msg[8] = NULL;
+ msg[9] = gcry_mpi_new(SM_MOD_LEN_BITS);
+ msg[10] = gcry_mpi_new(SM_MOD_LEN_BITS);
+
+ *msg2 = msg;
+}
+
+/*
+ * Initialize the fields of a SM message3.
+ * [0] = pa, [1] = qa, [2] = cp, [3] = d5, [4] = d6, [5] = ra,
+ * [6] = cr, [7] = d7
+ */
+void otrl_sm_msg3_init(gcry_mpi_t **msg3)
+{
+ gcry_mpi_t *msg = malloc(SM_MSG3_LEN * sizeof(gcry_mpi_t));
+ msg[0] = gcry_mpi_new(SM_MOD_LEN_BITS);
+ msg[1] = gcry_mpi_new(SM_MOD_LEN_BITS);
+ msg[2] = NULL;
+ msg[3] = gcry_mpi_new(SM_MOD_LEN_BITS);
+ msg[4] = gcry_mpi_new(SM_MOD_LEN_BITS);
+ msg[5] = gcry_mpi_new(SM_MOD_LEN_BITS);
+ msg[6] = NULL;
+ msg[7] = gcry_mpi_new(SM_MOD_LEN_BITS);
+
+ *msg3 = msg;
+}
+
+/*
+ * Initialize the fields of a SM message4.
+ * [0] = rb, [1] = cr, [2] = d7
+ */
+void otrl_sm_msg4_init(gcry_mpi_t **msg4)
+{
+ gcry_mpi_t *msg = malloc(SM_MSG4_LEN * sizeof(gcry_mpi_t));
+ msg[0] = gcry_mpi_new(SM_MOD_LEN_BITS);
+ msg[1] = NULL;
+ msg[2] = gcry_mpi_new(SM_MOD_LEN_BITS);
+
+ *msg4 = msg;
+}
+
+/*
+ * Deallocate the contents of a OtrlSMState (but not the OtrlSMState
+ * itself)
+ */
+void otrl_sm_state_free(OtrlSMState *smst)
+{
+ gcry_mpi_release(smst->secret);
+ gcry_mpi_release(smst->x2);
+ gcry_mpi_release(smst->x3);
+ gcry_mpi_release(smst->g1);
+ gcry_mpi_release(smst->g2);
+ gcry_mpi_release(smst->g3);
+ gcry_mpi_release(smst->g3o);
+ gcry_mpi_release(smst->p);
+ gcry_mpi_release(smst->q);
+ gcry_mpi_release(smst->pab);
+ gcry_mpi_release(smst->qab);
+ otrl_sm_state_new(smst);
+}
+
+/*
+ * Deallocate the contents of a message
+ */
+void otrl_sm_msg_free(gcry_mpi_t **message, int msglen)
+{
+ gcry_mpi_t *msg = *message;
+ int i;
+ for (i=0; i<msglen; i++) {
+ gcry_mpi_release(msg[i]);
+ }
+ free(msg);
+ *message = NULL;
+}
+
+static gcry_mpi_t randomExponent(void)
+{
+ unsigned char *secbuf = NULL;
+ gcry_mpi_t randexpon = NULL;
+
+ /* Generate a random exponent */
+ secbuf = gcry_random_bytes_secure(SM_MOD_LEN_BYTES, GCRY_STRONG_RANDOM);
+ gcry_mpi_scan(&randexpon, GCRYMPI_FMT_USG, secbuf, SM_MOD_LEN_BYTES, NULL);
+ gcry_free(secbuf);
+
+ return randexpon;
+}
+
+/*
+ * Hash one or two mpis. To hash only one mpi, b may be set to NULL.
+ */
+static gcry_error_t otrl_sm_hash(gcry_mpi_t* hash, int version,
+ const gcry_mpi_t a, const gcry_mpi_t b)
+{
+ unsigned char* input;
+ unsigned char output[SM_DIGEST_SIZE];
+ size_t sizea;
+ size_t sizeb;
+ size_t totalsize;
+ unsigned char* dataa;
+ unsigned char* datab;
+
+ gcry_mpi_aprint(GCRYMPI_FMT_USG, &dataa, &sizea, a);
+ totalsize = 1 + 4 + sizea;
+ if (b) {
+ gcry_mpi_aprint(GCRYMPI_FMT_USG, &datab, &sizeb, b);
+ totalsize += 4 + sizeb;
+ } else {
+ sizeb = 0;
+ }
+
+ input = malloc(totalsize);
+ input[0] = (unsigned char)version;
+ input[1] = (unsigned char)((sizea >> 24) & 0xFF);
+ input[2] = (unsigned char)((sizea >> 16) & 0xFF);
+ input[3] = (unsigned char)((sizea >> 8) & 0xFF);
+ input[4] = (unsigned char)(sizea & 0xFF);
+ memmove(input + 5, dataa, sizea);
+ if (b) {
+ input[5 + sizea] = (unsigned char)((sizeb >> 24) & 0xFF);
+ input[6 + sizea] = (unsigned char)((sizeb >> 16) & 0xFF);
+ input[7 + sizea] = (unsigned char)((sizeb >> 8) & 0xFF);
+ input[8 + sizea] = (unsigned char)(sizeb & 0xFF);
+ memmove(input + 9 + sizea, datab, sizeb);
+ }
+
+ gcry_md_hash_buffer(SM_HASH_ALGORITHM, output, input, totalsize);
+ gcry_mpi_scan(hash, GCRYMPI_FMT_USG, output, SM_DIGEST_SIZE, NULL);
+ free(input);
+ input = NULL;
+
+ /* free memory */
+ gcry_free(dataa);
+ if (b) gcry_free(datab);
+
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* This method should be passed a pointer to an uninitialized buffer,
+ * and a list of mpis with a list length. When returns, the buffer will
+ * point to newly-allocated memory (using malloc) containing a
+ * reversible serialization. */
+static gcry_error_t serialize_mpi_array(unsigned char **buffer, int *buflen,
+ unsigned int count, gcry_mpi_t *mpis)
+{
+ size_t totalsize = 0, lenp, nextsize;
+ unsigned int i, j;
+ size_t *list_sizes = malloc(count * sizeof(size_t));
+ unsigned char **tempbuffer = malloc(count * sizeof(unsigned char *));
+ unsigned char *bufp;
+
+ for (i=0; i<count; i++) {
+ gcry_mpi_aprint(GCRYMPI_FMT_USG, &(tempbuffer[i]), &(list_sizes[i]),
+ mpis[i]);
+ totalsize += list_sizes[i];
+ }
+
+ *buflen = (count+1)*4 + totalsize;
+ *buffer = malloc(*buflen * sizeof(char));
+
+ bufp = *buffer;
+ lenp = totalsize;
+
+ write_int(count);
+ for(i=0; i<count; i++)
+ {
+ nextsize = list_sizes[i];
+ write_int(nextsize);
+
+ for(j=0; j<nextsize; j++)
+ bufp[j] = tempbuffer[i][j];
+
+ bufp += nextsize;
+ lenp -= nextsize;
+ gcry_free(tempbuffer[i]);
+ }
+ free(tempbuffer);
+ free(list_sizes);
+
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Takes a buffer containing serialized and concatenated mpis
+ * and converts it to an array of gcry_mpi_t structs.
+ * The buffer is assumed to consist of a 4-byte int containing the
+ * number of mpis in the array, followed by {size, data} pairs for
+ * each mpi. If malformed, method returns GCRY_ERROR_INV_VALUE */
+static gcry_error_t unserialize_mpi_array(gcry_mpi_t **mpis,
+ unsigned int expcount, const unsigned char *buffer, const int buflen)
+{
+ int i;
+ int lenp = buflen;
+ unsigned int thecount = 0;
+ const unsigned char* bufp = buffer;
+ *mpis = NULL;
+
+ read_int(thecount);
+ if (thecount != expcount) goto invval;
+
+ *mpis = malloc(thecount * sizeof(gcry_mpi_t));
+
+ for (i=0; i<thecount; i++) {
+ (*mpis)[i] = NULL;
+ }
+
+ for (i=0; i<thecount; i++) {
+ read_mpi((*mpis)[i]);
+ }
+
+ return gcry_error(GPG_ERR_NO_ERROR);
+
+invval:
+ if (*mpis) {
+ for (i=0; i<thecount; i++) {
+ gcry_mpi_release((*mpis)[i]);
+ }
+ free(*mpis);
+ *mpis = NULL;
+ }
+ return gcry_error(GPG_ERR_INV_VALUE);
+}
+
+/* Check that an MPI is in the right range to be a (non-unit) group
+ * element */
+static int check_group_elem(gcry_mpi_t g)
+{
+ if (gcry_mpi_cmp_ui(g, 2) < 0 ||
+ gcry_mpi_cmp(g, SM_MODULUS_MINUS_2) > 0) {
+ return 1;
+ }
+ return 0;
+}
+
+/* Check that an MPI is in the right range to be a (non-zero) exponent */
+static int check_expon(gcry_mpi_t x)
+{
+ if (gcry_mpi_cmp_ui(x, 1) < 0 ||
+ gcry_mpi_cmp(x, SM_ORDER) >= 0) {
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Proof of knowledge of a discrete logarithm
+ */
+static gcry_error_t otrl_sm_proof_know_log(gcry_mpi_t *c, gcry_mpi_t *d, const gcry_mpi_t g, const gcry_mpi_t x, int version)
+{
+ gcry_mpi_t r = randomExponent();
+ gcry_mpi_t temp = gcry_mpi_new(SM_MOD_LEN_BITS);
+ gcry_mpi_powm(temp, g, r, SM_MODULUS);
+ otrl_sm_hash(c, version, temp, NULL);
+ gcry_mpi_mulm(temp, x, *c, SM_ORDER);
+ gcry_mpi_subm(*d, r, temp, SM_ORDER);
+ gcry_mpi_release(temp);
+ gcry_mpi_release(r);
+
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/*
+ * Verify a proof of knowledge of a discrete logarithm. Checks that c = h(g^d x^c)
+ */
+static int otrl_sm_check_know_log(const gcry_mpi_t c, const gcry_mpi_t d, const gcry_mpi_t g, const gcry_mpi_t x, int version)
+{
+ int comp;
+
+ gcry_mpi_t gd = gcry_mpi_new(SM_MOD_LEN_BITS); /* g^d */
+ gcry_mpi_t xc = gcry_mpi_new(SM_MOD_LEN_BITS); /* x^c */
+ gcry_mpi_t gdxc = gcry_mpi_new(SM_MOD_LEN_BITS); /* (g^d x^c) */
+ gcry_mpi_t hgdxc = NULL; /* h(g^d x^c) */
+
+ gcry_mpi_powm(gd, g, d, SM_MODULUS);
+ gcry_mpi_powm(xc, x, c, SM_MODULUS);
+ gcry_mpi_mulm(gdxc, gd, xc, SM_MODULUS);
+ otrl_sm_hash(&hgdxc, version, gdxc, NULL);
+
+ comp = gcry_mpi_cmp(hgdxc, c);
+ gcry_mpi_release(gd);
+ gcry_mpi_release(xc);
+ gcry_mpi_release(gdxc);
+ gcry_mpi_release(hgdxc);
+
+ return comp;
+}
+
+/*
+ * Proof of knowledge of coordinates with first components being equal
+ */
+static gcry_error_t otrl_sm_proof_equal_coords(gcry_mpi_t *c, gcry_mpi_t *d1, gcry_mpi_t *d2, const OtrlSMState *state, const gcry_mpi_t r, int version)
+{
+ gcry_mpi_t r1 = randomExponent();
+ gcry_mpi_t r2 = randomExponent();
+ gcry_mpi_t temp1 = gcry_mpi_new(SM_MOD_LEN_BITS);
+ gcry_mpi_t temp2 = gcry_mpi_new(SM_MOD_LEN_BITS);
+
+ /* Compute the value of c, as c = h(g3^r1, g1^r1 g2^r2) */
+ gcry_mpi_powm(temp1, state->g1, r1, SM_MODULUS);
+ gcry_mpi_powm(temp2, state->g2, r2, SM_MODULUS);
+ gcry_mpi_mulm(temp2, temp1, temp2, SM_MODULUS);
+ gcry_mpi_powm(temp1, state->g3, r1, SM_MODULUS);
+ otrl_sm_hash(c, version, temp1, temp2);
+
+ /* Compute the d values, as d1 = r1 - r c, d2 = r2 - secret c */
+ gcry_mpi_mulm(temp1, r, *c, SM_ORDER);
+ gcry_mpi_subm(*d1, r1, temp1, SM_ORDER);
+
+ gcry_mpi_mulm(temp1, state->secret, *c, SM_ORDER);
+ gcry_mpi_subm(*d2, r2, temp1, SM_ORDER);
+
+ /* All clear */
+ gcry_mpi_release(r1);
+ gcry_mpi_release(r2);
+ gcry_mpi_release(temp1);
+ gcry_mpi_release(temp2);
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/*
+ * Verify a proof of knowledge of coordinates with first components being equal
+ */
+static gcry_error_t otrl_sm_check_equal_coords(const gcry_mpi_t c, const gcry_mpi_t d1, const gcry_mpi_t d2, const gcry_mpi_t p, const gcry_mpi_t q, const OtrlSMState *state, int version)
+{
+ int comp;
+
+ gcry_mpi_t temp1 = gcry_mpi_new(SM_MOD_LEN_BITS);
+ gcry_mpi_t temp2 = gcry_mpi_new(SM_MOD_LEN_BITS);
+ gcry_mpi_t temp3 = gcry_mpi_new(SM_MOD_LEN_BITS);
+ gcry_mpi_t cprime = NULL;
+
+ /* To verify, we test that hash(g3^d1 * p^c, g1^d1 * g2^d2 * q^c) = c
+ * If indeed c = hash(g3^r1, g1^r1 g2^r2), d1 = r1 - r*c,
+ * d2 = r2 - secret*c. And if indeed p = g3^r, q = g1^r * g2^secret
+ * Then we should have that:
+ * hash(g3^d1 * p^c, g1^d1 * g2^d2 * q^c)
+ * = hash(g3^(r1 - r*c + r*c), g1^(r1 - r*c + q*c) *
+ * g2^(r2 - secret*c + secret*c))
+ * = hash(g3^r1, g1^r1 g2^r2)
+ * = c
+ */
+ gcry_mpi_powm(temp2, state->g3, d1, SM_MODULUS);
+ gcry_mpi_powm(temp3, p, c, SM_MODULUS);
+ gcry_mpi_mulm(temp1, temp2, temp3, SM_MODULUS);
+
+ gcry_mpi_powm(temp2, state->g1, d1, SM_MODULUS);
+ gcry_mpi_powm(temp3, state->g2, d2, SM_MODULUS);
+ gcry_mpi_mulm(temp2, temp2, temp3, SM_MODULUS);
+ gcry_mpi_powm(temp3, q, c, SM_MODULUS);
+ gcry_mpi_mulm(temp2, temp3, temp2, SM_MODULUS);
+
+ otrl_sm_hash(&cprime, version, temp1, temp2);
+
+ comp = gcry_mpi_cmp(c, cprime);
+ gcry_mpi_release(temp1);
+ gcry_mpi_release(temp2);
+ gcry_mpi_release(temp3);
+ gcry_mpi_release(cprime);
+
+ return comp;
+}
+
+/*
+ * Proof of knowledge of logs with exponents being equal
+ */
+static gcry_error_t otrl_sm_proof_equal_logs(gcry_mpi_t *c, gcry_mpi_t *d, OtrlSMState *state, int version)
+{
+ gcry_mpi_t r = randomExponent();
+ gcry_mpi_t temp1 = gcry_mpi_new(SM_MOD_LEN_BITS);
+ gcry_mpi_t temp2 = gcry_mpi_new(SM_MOD_LEN_BITS);
+
+ /* Compute the value of c, as c = h(g1^r, (Qa/Qb)^r) */
+ gcry_mpi_powm(temp1, state->g1, r, SM_MODULUS);
+ gcry_mpi_powm(temp2, state->qab, r, SM_MODULUS);
+ otrl_sm_hash(c, version, temp1, temp2);
+
+ /* Compute the d values, as d = r - x3 c */
+ gcry_mpi_mulm(temp1, state->x3, *c, SM_ORDER);
+ gcry_mpi_subm(*d, r, temp1, SM_ORDER);
+
+ /* All clear */
+ gcry_mpi_release(r);
+ gcry_mpi_release(temp1);
+ gcry_mpi_release(temp2);
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/*
+ * Verify a proof of knowledge of logs with exponents being equal
+ */
+static gcry_error_t otrl_sm_check_equal_logs(const gcry_mpi_t c, const gcry_mpi_t d, const gcry_mpi_t r, const OtrlSMState *state, int version)
+{
+ int comp;
+
+ gcry_mpi_t temp1 = gcry_mpi_new(SM_MOD_LEN_BITS);
+ gcry_mpi_t temp2 = gcry_mpi_new(SM_MOD_LEN_BITS);
+ gcry_mpi_t temp3 = gcry_mpi_new(SM_MOD_LEN_BITS);
+ gcry_mpi_t cprime = NULL;
+
+ /* Here, we recall the exponents used to create g3.
+ * If we have previously seen g3o = g1^x where x is unknown
+ * during the DH exchange to produce g3, then we may proceed with:
+ *
+ * To verify, we test that hash(g1^d * g3o^c, qab^d * r^c) = c
+ * If indeed c = hash(g1^r1, qab^r1), d = r1- x * c
+ * And if indeed r = qab^x
+ * Then we should have that:
+ * hash(g1^d * g3o^c, qab^d r^c)
+ * = hash(g1^(r1 - x*c + x*c), qab^(r1 - x*c + x*c))
+ * = hash(g1^r1, qab^r1)
+ * = c
+ */
+ gcry_mpi_powm(temp2, state->g1, d, SM_MODULUS);
+ gcry_mpi_powm(temp3, state->g3o, c, SM_MODULUS);
+ gcry_mpi_mulm(temp1, temp2, temp3, SM_MODULUS);
+
+ gcry_mpi_powm(temp3, state->qab, d, SM_MODULUS);
+ gcry_mpi_powm(temp2, r, c, SM_MODULUS);
+ gcry_mpi_mulm(temp2, temp3, temp2, SM_MODULUS);
+
+ otrl_sm_hash(&cprime, version, temp1, temp2);
+
+ comp = gcry_mpi_cmp(c, cprime);
+ gcry_mpi_release(temp1);
+ gcry_mpi_release(temp2);
+ gcry_mpi_release(temp3);
+ gcry_mpi_release(cprime);
+
+ return comp;
+}
+
+/* Create first message in SMP exchange. Input is Alice's secret value
+ * which this protocol aims to compare to Bob's. Output is a serialized
+ * mpi array whose elements correspond to the following:
+ * [0] = g2a, Alice's half of DH exchange to determine g2
+ * [1] = c2, [2] = d2, Alice's ZK proof of knowledge of g2a exponent
+ * [3] = g3a, Alice's half of DH exchange to determine g3
+ * [4] = c3, [5] = d3, Alice's ZK proof of knowledge of g3a exponent */
+gcry_error_t otrl_sm_step1(OtrlSMAliceState *astate,
+ const unsigned char* secret, int secretlen,
+ unsigned char** output, int* outputlen)
+{
+ /* Initialize the sm state or update the secret */
+ gcry_mpi_t secret_mpi = NULL;
+ gcry_mpi_t *msg1;
+
+ *output = NULL;
+ *outputlen = 0;
+
+ gcry_mpi_scan(&secret_mpi, GCRYMPI_FMT_USG, secret, secretlen, NULL);
+
+ if (! astate->g1) {
+ otrl_sm_state_init(astate);
+ }
+ gcry_mpi_set(astate->secret, secret_mpi);
+ gcry_mpi_release(secret_mpi);
+ astate->received_question = 0;
+
+ otrl_sm_msg1_init(&msg1);
+
+ astate->x2 = randomExponent();
+ astate->x3 = randomExponent();
+
+ gcry_mpi_powm(msg1[0], astate->g1, astate->x2, SM_MODULUS);
+ otrl_sm_proof_know_log(&(msg1[1]), &(msg1[2]), astate->g1, astate->x2, 1);
+
+ gcry_mpi_powm(msg1[3], astate->g1, astate->x3, SM_MODULUS);
+ otrl_sm_proof_know_log(&(msg1[4]), &(msg1[5]), astate->g1, astate->x3, 2);
+
+ serialize_mpi_array(output, outputlen, SM_MSG1_LEN, msg1);
+ otrl_sm_msg_free(&msg1, SM_MSG1_LEN);
+ astate->sm_prog_state = OTRL_SMP_PROG_OK;
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Receive the first message in SMP exchange, which was generated by
+ * otrl_sm_step1. Input is saved until the user inputs their secret
+ * information. No output. */
+gcry_error_t otrl_sm_step2a(OtrlSMBobState *bstate, const unsigned char* input, const int inputlen, int received_question)
+{
+ gcry_mpi_t *msg1;
+ gcry_error_t err;
+
+ /* Initialize the sm state if needed */
+ if (! bstate->g1) {
+ otrl_sm_state_init(bstate);
+ }
+ bstate->received_question = received_question;
+ bstate->sm_prog_state = OTRL_SMP_PROG_CHEATED;
+
+ /* Read from input to find the mpis */
+ err = unserialize_mpi_array(&msg1, SM_MSG1_LEN, input, inputlen);
+
+ if (err != gcry_error(GPG_ERR_NO_ERROR)) return err;
+
+ if (check_group_elem(msg1[0]) || check_expon(msg1[2]) ||
+ check_group_elem(msg1[3]) || check_expon(msg1[5])) {
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
+
+ /* Store Alice's g3a value for later in the protocol */
+ gcry_mpi_set(bstate->g3o, msg1[3]);
+
+ /* Verify Alice's proofs */
+ if (otrl_sm_check_know_log(msg1[1], msg1[2], bstate->g1, msg1[0], 1) ||
+ otrl_sm_check_know_log(msg1[4], msg1[5], bstate->g1, msg1[3], 2)) {
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
+
+ /* Create Bob's half of the generators g2 and g3 */
+ bstate->x2 = randomExponent();
+ bstate->x3 = randomExponent();
+
+ /* Combine the two halves from Bob and Alice and determine g2 and g3 */
+ gcry_mpi_powm(bstate->g2, msg1[0], bstate->x2, SM_MODULUS);
+ gcry_mpi_powm(bstate->g3, msg1[3], bstate->x3, SM_MODULUS);
+
+ bstate->sm_prog_state = OTRL_SMP_PROG_OK;
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Create second message in SMP exchange. Input is Bob's secret value.
+ * Information from earlier steps in the exchange is taken from Bob's
+ * state. Output is a serialized mpi array whose elements correspond
+ * to the following:
+ * [0] = g2b, Bob's half of DH exchange to determine g2
+ * [1] = c2, [2] = d2, Bob's ZK proof of knowledge of g2b exponent
+ * [3] = g3b, Bob's half of DH exchange to determine g3
+ * [4] = c3, [5] = d3, Bob's ZK proof of knowledge of g3b exponent
+ * [6] = pb, [7] = qb, Bob's halves of the (Pa/Pb) and (Qa/Qb) values
+ * [8] = cp, [9] = d5, [10] = d6, Bob's ZK proof that pb, qb formed correctly */
+gcry_error_t otrl_sm_step2b(OtrlSMBobState *bstate, const unsigned char* secret, int secretlen, unsigned char **output, int* outputlen)
+{
+ /* Convert the given secret to the proper form and store it */
+ gcry_mpi_t r, qb1, qb2;
+ gcry_mpi_t *msg2;
+ gcry_mpi_t secret_mpi = NULL;
+
+ *output = NULL;
+ *outputlen = 0;
+
+ gcry_mpi_scan(&secret_mpi, GCRYMPI_FMT_USG, secret, secretlen, NULL);
+ gcry_mpi_set(bstate->secret, secret_mpi);
+ gcry_mpi_release(secret_mpi);
+
+ otrl_sm_msg2_init(&msg2);
+
+ gcry_mpi_powm(msg2[0], bstate->g1, bstate->x2, SM_MODULUS);
+ otrl_sm_proof_know_log(&(msg2[1]), &(msg2[2]), bstate->g1, bstate->x2, 3);
+
+ gcry_mpi_powm(msg2[3], bstate->g1, bstate->x3, SM_MODULUS);
+ otrl_sm_proof_know_log(&(msg2[4]), &(msg2[5]), bstate->g1, bstate->x3, 4);
+
+ /* Calculate P and Q values for Bob */
+ r = randomExponent();
+ qb1 = gcry_mpi_new(SM_MOD_LEN_BITS);
+ qb2 = gcry_mpi_new(SM_MOD_LEN_BITS);
+ gcry_mpi_powm(bstate->p, bstate->g3, r, SM_MODULUS);
+ gcry_mpi_set(msg2[6], bstate->p);
+ gcry_mpi_powm(qb1, bstate->g1, r, SM_MODULUS);
+ gcry_mpi_powm(qb2, bstate->g2, bstate->secret, SM_MODULUS);
+ gcry_mpi_mulm(bstate->q, qb1, qb2, SM_MODULUS);
+ gcry_mpi_set(msg2[7], bstate->q);
+
+ otrl_sm_proof_equal_coords(&(msg2[8]), &(msg2[9]), &(msg2[10]), bstate, r, 5);
+
+ /* Convert to serialized form */
+ serialize_mpi_array(output, outputlen, SM_MSG2_LEN, msg2);
+
+ /* Free up memory for unserialized and intermediate values */
+ gcry_mpi_release(r);
+ gcry_mpi_release(qb1);
+ gcry_mpi_release(qb2);
+ otrl_sm_msg_free(&msg2, SM_MSG2_LEN);
+
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Create third message in SMP exchange. Input is a message generated
+ * by otrl_sm_step2b. Output is a serialized mpi array whose elements
+ * correspond to the following:
+ * [0] = pa, [1] = qa, Alice's halves of the (Pa/Pb) and (Qa/Qb) values
+ * [2] = cp, [3] = d5, [4] = d6, Alice's ZK proof that pa, qa formed correctly
+ * [5] = ra, calculated as (Qa/Qb)^x3 where x3 is the exponent used in g3a
+ * [6] = cr, [7] = d7, Alice's ZK proof that ra is formed correctly */
+gcry_error_t otrl_sm_step3(OtrlSMAliceState *astate, const unsigned char* input, const int inputlen, unsigned char **output, int* outputlen)
+{
+ /* Read from input to find the mpis */
+ gcry_mpi_t r, qa1, qa2, inv;
+ gcry_mpi_t *msg2;
+ gcry_mpi_t *msg3;
+ gcry_error_t err;
+
+ *output = NULL;
+ *outputlen = 0;
+ astate->sm_prog_state = OTRL_SMP_PROG_CHEATED;
+
+ err = unserialize_mpi_array(&msg2, SM_MSG2_LEN, input, inputlen);
+ if (err != gcry_error(GPG_ERR_NO_ERROR)) return err;
+
+ if (check_group_elem(msg2[0]) || check_group_elem(msg2[3]) ||
+ check_group_elem(msg2[6]) || check_group_elem(msg2[7]) ||
+ check_expon(msg2[2]) || check_expon(msg2[5]) ||
+ check_expon(msg2[9]) || check_expon(msg2[10])) {
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
+
+ otrl_sm_msg3_init(&msg3);
+
+ /* Store Bob's g3a value for later in the protocol */
+ gcry_mpi_set(astate->g3o, msg2[3]);
+
+ /* Verify Bob's knowledge of discreet log proofs */
+ if (otrl_sm_check_know_log(msg2[1], msg2[2], astate->g1, msg2[0], 3) ||
+ otrl_sm_check_know_log(msg2[4], msg2[5], astate->g1, msg2[3], 4)) {
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
+
+ /* Combine the two halves from Bob and Alice and determine g2 and g3 */
+ gcry_mpi_powm(astate->g2, msg2[0], astate->x2, SM_MODULUS);
+ gcry_mpi_powm(astate->g3, msg2[3], astate->x3, SM_MODULUS);
+
+ /* Verify Bob's coordinate equality proof */
+ if (otrl_sm_check_equal_coords(msg2[8], msg2[9], msg2[10], msg2[6], msg2[7], astate, 5))
+ return gcry_error(GPG_ERR_INV_VALUE);
+
+ /* Calculate P and Q values for Alice */
+ r = randomExponent();
+ qa1 = gcry_mpi_new(SM_MOD_LEN_BITS);
+ qa2 = gcry_mpi_new(SM_MOD_LEN_BITS);
+ gcry_mpi_powm(astate->p, astate->g3, r, SM_MODULUS);
+ gcry_mpi_set(msg3[0], astate->p);
+ gcry_mpi_powm(qa1, astate->g1, r, SM_MODULUS);
+ gcry_mpi_powm(qa2, astate->g2, astate->secret, SM_MODULUS);
+ gcry_mpi_mulm(astate->q, qa1, qa2, SM_MODULUS);
+ gcry_mpi_set(msg3[1], astate->q);
+
+ otrl_sm_proof_equal_coords(&(msg3[2]), &(msg3[3]), &(msg3[4]), astate, r, 6);
+
+ /* Calculate Ra and proof */
+ inv = gcry_mpi_new(SM_MOD_LEN_BITS);
+ gcry_mpi_invm(inv, msg2[6], SM_MODULUS);
+ gcry_mpi_mulm(astate->pab, astate->p, inv, SM_MODULUS);
+ gcry_mpi_invm(inv, msg2[7], SM_MODULUS);
+ gcry_mpi_mulm(astate->qab, astate->q, inv, SM_MODULUS);
+ gcry_mpi_powm(msg3[5], astate->qab, astate->x3, SM_MODULUS);
+ otrl_sm_proof_equal_logs(&(msg3[6]), &(msg3[7]), astate, 7);
+
+ serialize_mpi_array(output, outputlen, SM_MSG3_LEN, msg3);
+ otrl_sm_msg_free(&msg2, SM_MSG2_LEN);
+ otrl_sm_msg_free(&msg3, SM_MSG3_LEN);
+
+ gcry_mpi_release(r);
+ gcry_mpi_release(qa1);
+ gcry_mpi_release(qa2);
+ gcry_mpi_release(inv);
+
+ astate->sm_prog_state = OTRL_SMP_PROG_OK;
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Create final message in SMP exchange. Input is a message generated
+ * by otrl_sm_step3. Output is a serialized mpi array whose elements
+ * correspond to the following:
+ * [0] = rb, calculated as (Qa/Qb)^x3 where x3 is the exponent used in g3b
+ * [1] = cr, [2] = d7, Bob's ZK proof that rb is formed correctly
+ * This method also checks if Alice and Bob's secrets were the same. If
+ * so, it returns NO_ERROR. If the secrets differ, an INV_VALUE error is
+ * returned instead. */
+gcry_error_t otrl_sm_step4(OtrlSMBobState *bstate, const unsigned char* input, const int inputlen, unsigned char **output, int* outputlen)
+{
+ /* Read from input to find the mpis */
+ int comp;
+ gcry_mpi_t inv, rab;
+ gcry_mpi_t *msg3;
+ gcry_mpi_t *msg4;
+ gcry_error_t err;
+ err = unserialize_mpi_array(&msg3, SM_MSG3_LEN, input, inputlen);
+
+ *output = NULL;
+ *outputlen = 0;
+ bstate->sm_prog_state = OTRL_SMP_PROG_CHEATED;
+
+ if (err != gcry_error(GPG_ERR_NO_ERROR)) return err;
+
+ otrl_sm_msg4_init(&msg4);
+
+ if (check_group_elem(msg3[0]) || check_group_elem(msg3[1]) ||
+ check_group_elem(msg3[5]) || check_expon(msg3[3]) ||
+ check_expon(msg3[4]) || check_expon(msg3[7])) {
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
+
+ /* Verify Alice's coordinate equality proof */
+ if (otrl_sm_check_equal_coords(msg3[2], msg3[3], msg3[4], msg3[0], msg3[1], bstate, 6))
+ return gcry_error(GPG_ERR_INV_VALUE);
+
+ /* Find Pa/Pb and Qa/Qb */
+ inv = gcry_mpi_new(SM_MOD_LEN_BITS);
+ gcry_mpi_invm(inv, bstate->p, SM_MODULUS);
+ gcry_mpi_mulm(bstate->pab, msg3[0], inv, SM_MODULUS);
+ gcry_mpi_invm(inv, bstate->q, SM_MODULUS);
+ gcry_mpi_mulm(bstate->qab, msg3[1], inv, SM_MODULUS);
+
+ /* Verify Alice's log equality proof */
+ if (otrl_sm_check_equal_logs(msg3[6], msg3[7], msg3[5], bstate, 7))
+ return gcry_error(GPG_ERR_INV_VALUE);
+
+ /* Calculate Rb and proof */
+ gcry_mpi_powm(msg4[0], bstate->qab, bstate->x3, SM_MODULUS);
+ otrl_sm_proof_equal_logs(&(msg4[1]), &(msg4[2]), bstate, 8);
+
+ serialize_mpi_array(output, outputlen, SM_MSG4_LEN, msg4);
+
+ /* Calculate Rab and verify that secrets match */
+ rab = gcry_mpi_new(SM_MOD_LEN_BITS);
+ gcry_mpi_powm(rab, msg3[5], bstate->x3, SM_MODULUS);
+ comp = gcry_mpi_cmp(rab, bstate->pab);
+
+ /* Clean up everything allocated in this step */
+ otrl_sm_msg_free(&msg3, SM_MSG3_LEN);
+ otrl_sm_msg_free(&msg4, SM_MSG4_LEN);
+ gcry_mpi_release(rab);
+ gcry_mpi_release(inv);
+
+ bstate->sm_prog_state = comp ? OTRL_SMP_PROG_FAILED :
+ OTRL_SMP_PROG_SUCCEEDED;
+
+ if (comp)
+ return gcry_error(GPG_ERR_INV_VALUE);
+ else
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/* Receives the final SMP message, which was generated in otrl_sm_step.
+ * This method checks if Alice and Bob's secrets were the same. If
+ * so, it returns NO_ERROR. If the secrets differ, an INV_VALUE error is
+ * returned instead. */
+gcry_error_t otrl_sm_step5(OtrlSMAliceState *astate, const unsigned char* input, const int inputlen)
+{
+ /* Read from input to find the mpis */
+ int comp;
+ gcry_mpi_t rab;
+ gcry_mpi_t *msg4;
+ gcry_error_t err;
+ err = unserialize_mpi_array(&msg4, SM_MSG4_LEN, input, inputlen);
+ astate->sm_prog_state = OTRL_SMP_PROG_CHEATED;
+
+ if (err != gcry_error(GPG_ERR_NO_ERROR)) return err;
+
+ if (check_group_elem(msg4[0]) || check_expon(msg4[2])) {
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
+
+ /* Verify Bob's log equality proof */
+ if (otrl_sm_check_equal_logs(msg4[1], msg4[2], msg4[0], astate, 8))
+ return gcry_error(GPG_ERR_INV_VALUE);
+
+ /* Calculate Rab and verify that secrets match */
+ rab = gcry_mpi_new(SM_MOD_LEN_BITS);
+ gcry_mpi_powm(rab, msg4[0], astate->x3, SM_MODULUS);
+
+ comp = gcry_mpi_cmp(rab, astate->pab);
+ gcry_mpi_release(rab);
+ otrl_sm_msg_free(&msg4, SM_MSG4_LEN);
+
+ astate->sm_prog_state = comp ? OTRL_SMP_PROG_FAILED :
+ OTRL_SMP_PROG_SUCCEEDED;
+
+ if (comp)
+ return gcry_error(GPG_ERR_INV_VALUE);
+ else
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
diff --git a/libotr-3.2.0/src/sm.h b/libotr-3.2.0/src/sm.h
new file mode 100644
index 0000000..2e94f07
--- /dev/null
+++ b/libotr-3.2.0/src/sm.h
@@ -0,0 +1,83 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@cypherpunks.ca>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General
+ * Public License as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __SM_H__
+#define __SM_H__
+
+#include <gcrypt.h>
+
+#define SM_HASH_ALGORITHM GCRY_MD_SHA256
+#define SM_DIGEST_SIZE 32
+
+typedef enum {
+ OTRL_SMP_EXPECT1,
+ OTRL_SMP_EXPECT2,
+ OTRL_SMP_EXPECT3,
+ OTRL_SMP_EXPECT4,
+ OTRL_SMP_EXPECT5
+} NextExpectedSMP;
+
+typedef enum {
+ OTRL_SMP_PROG_OK = 0, /* All is going fine so far */
+ OTRL_SMP_PROG_CHEATED = -2, /* Some verification failed */
+ OTRL_SMP_PROG_FAILED = -1, /* The secrets didn't match */
+ OTRL_SMP_PROG_SUCCEEDED = 1 /* The SMP completed successfully */
+} OtrlSMProgState;
+
+typedef struct {
+ gcry_mpi_t secret, x2, x3, g1, g2, g3, g3o, p, q, pab, qab;
+ NextExpectedSMP nextExpected;
+ int received_question; /* 1 if we received a question in an SMP1Q TLV */
+ OtrlSMProgState sm_prog_state;
+} OtrlSMState;
+
+typedef OtrlSMState OtrlSMAliceState;
+typedef OtrlSMState OtrlSMBobState;
+
+/*
+ * Call this once, at plugin load time. It sets up the modulus and
+ * generator MPIs.
+ */
+void otrl_sm_init(void);
+
+/*
+ * Initialize the fields of a SM state.
+ */
+void otrl_sm_state_new(OtrlSMState *smst);
+
+/*
+ * Initialize the fields of a SM state. Called the first time that
+ * a user begins an SMP session.
+ */
+void otrl_sm_state_init(OtrlSMState *smst);
+
+/*
+ * Deallocate the contents of a OtrlSMState (but not the OtrlSMState
+ * itself)
+ */
+void otrl_sm_state_free(OtrlSMState *smst);
+
+gcry_error_t otrl_sm_step1(OtrlSMAliceState *astate, const unsigned char* secret, int secretlen, unsigned char** output, int* outputlen);
+gcry_error_t otrl_sm_step2a(OtrlSMBobState *bstate, const unsigned char* input, const int inputlen, int received_question);
+gcry_error_t otrl_sm_step2b(OtrlSMBobState *bstate, const unsigned char* secret, int secretlen, unsigned char **output, int* outputlen);
+gcry_error_t otrl_sm_step3(OtrlSMAliceState *astate, const unsigned char* input, const int inputlen, unsigned char **output, int* outputlen);
+gcry_error_t otrl_sm_step4(OtrlSMBobState *bstate, const unsigned char* input, const int inputlen, unsigned char **output, int* outputlen);
+gcry_error_t otrl_sm_step5(OtrlSMAliceState *astate, const unsigned char* input, const int inputlen);
+
+#endif
diff --git a/libotr-3.2.0/src/tlv.c b/libotr-3.2.0/src/tlv.c
new file mode 100644
index 0000000..0cea7b5
--- /dev/null
+++ b/libotr-3.2.0/src/tlv.c
@@ -0,0 +1,108 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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,
+ const 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(const 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(const 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, const 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. (The tlvs argument isn't const because the return type
+ * needs to be non-const.) */
+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/libotr-3.2.0/src/tlv.h b/libotr-3.2.0/src/tlv.h
new file mode 100644
index 0000000..affe0d5
--- /dev/null
+++ b/libotr-3.2.0/src/tlv.h
@@ -0,0 +1,70 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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
+
+/* The message contains a step in the Socialist Millionaires' Protocol. */
+#define OTRL_TLV_SMP1 0x0002
+#define OTRL_TLV_SMP2 0x0003
+#define OTRL_TLV_SMP3 0x0004
+#define OTRL_TLV_SMP4 0x0005
+#define OTRL_TLV_SMP_ABORT 0x0006
+/* Like OTRL_TLV_SMP1, but there's a question for the buddy at the
+ * beginning */
+#define OTRL_TLV_SMP1Q 0x0007
+
+/* Make a single TLV, copying the supplied data */
+OtrlTLV *otrl_tlv_new(unsigned short type, unsigned short len,
+ const unsigned char *data);
+
+/* Construct a chain of TLVs from the given data */
+OtrlTLV *otrl_tlv_parse(const 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(const OtrlTLV *tlv);
+
+/* Serialize a chain of TLVs. The supplied buffer must already be large
+ * enough. */
+void otrl_tlv_serialize(unsigned char *buf, const OtrlTLV *tlv);
+
+/* Return the first TLV with the given type in the chain, or NULL if one
+ * isn't found. (The tlvs argument isn't const because the return type
+ * needs to be non-const.) */
+OtrlTLV *otrl_tlv_find(OtrlTLV *tlvs, unsigned short type);
+
+#endif
diff --git a/libotr-3.2.0/src/userstate.c b/libotr-3.2.0/src/userstate.c
new file mode 100644
index 0000000..6de95b8
--- /dev/null
+++ b/libotr-3.2.0/src/userstate.c
@@ -0,0 +1,51 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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/libotr-3.2.0/src/userstate.h b/libotr-3.2.0/src/userstate.h
new file mode 100644
index 0000000..8525df0
--- /dev/null
+++ b/libotr-3.2.0/src/userstate.h
@@ -0,0 +1,45 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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-t.h"
+
+struct s_OtrlUserState {
+ ConnContext *context_root;
+ OtrlPrivKey *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/libotr-3.2.0/src/version.h b/libotr-3.2.0/src/version.h
new file mode 100644
index 0000000..11cb586
--- /dev/null
+++ b/libotr-3.2.0/src/version.h
@@ -0,0 +1,29 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov
+ * <otr@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 "3.2.0"
+
+#define OTRL_VERSION_MAJOR 3
+#define OTRL_VERSION_MINOR 2
+#define OTRL_VERSION_SUB 0
+
+#endif