diff options
author | admin@progandy.co.cc <admin@progandy.co.cc@eced67a3-f377-a0ae-92ae-d6de1850b05a> | 2010-08-19 20:12:06 +0000 |
---|---|---|
committer | admin@progandy.co.cc <admin@progandy.co.cc@eced67a3-f377-a0ae-92ae-d6de1850b05a> | 2010-08-19 20:12:06 +0000 |
commit | 11e5e8749eb7d4d3cfebfa49bbb7ea4624608647 (patch) | |
tree | c773fee94a63a078e5cb59bdbfd3165f1830b7ea /libotr-3.2.0/src | |
parent | c7e64af067562167b6941f3ad8383e3ceb817633 (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')
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 |