]> wagner.pp.ru Git - openssl-gost/engine.git/commitdiff
Initial commit
authorDmitry Belyavsky <beldmit@manul.localdomain>
Fri, 14 Aug 2015 17:50:01 +0000 (20:50 +0300)
committerDmitry Belyavsky <beldmit@manul.localdomain>
Fri, 14 Aug 2015 17:50:01 +0000 (20:50 +0300)
28 files changed:
Makefile [new file with mode: 0644]
README.gost [new file with mode: 0644]
e_gost_err.c [new file with mode: 0644]
e_gost_err.h [new file with mode: 0644]
e_gost_err.proto [new file with mode: 0644]
gost.ec [new file with mode: 0644]
gost2001.c [new file with mode: 0644]
gost2001_keyx.c [new file with mode: 0644]
gost2001_keyx.h [new file with mode: 0644]
gost89.c [new file with mode: 0644]
gost89.h [new file with mode: 0644]
gost94_keyx.c [new file with mode: 0644]
gost_ameth.c [new file with mode: 0644]
gost_asn1.c [new file with mode: 0644]
gost_crypt.c [new file with mode: 0644]
gost_ctl.c [new file with mode: 0644]
gost_eng.c [new file with mode: 0644]
gost_keywrap.c [new file with mode: 0644]
gost_keywrap.h [new file with mode: 0644]
gost_lcl.h [new file with mode: 0644]
gost_md.c [new file with mode: 0644]
gost_params.c [new file with mode: 0644]
gost_params.h [new file with mode: 0644]
gost_pmeth.c [new file with mode: 0644]
gost_sign.c [new file with mode: 0644]
gosthash.c [new file with mode: 0644]
gosthash.h [new file with mode: 0644]
gostsum.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..17e1efb
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,276 @@
+DIR=ccgost
+TOP=../..
+CC=cc
+INCLUDES= -I../../include
+CFLAG=-g
+MAKEFILE= Makefile
+AR= ar r
+CFLAGS= $(INCLUDES) $(CFLAG)
+LIB=$(TOP)/libcrypto.a
+
+LIBSRC= gost2001.c gost2001_keyx.c gost89.c gost94_keyx.c gost_ameth.c gost_asn1.c gost_crypt.c gost_ctl.c gost_eng.c gosthash.c gost_keywrap.c gost_md.c gost_params.c gost_pmeth.c gost_sign.c
+
+LIBOBJ= e_gost_err.o gost2001_keyx.o gost2001.o gost89.o gost94_keyx.o gost_ameth.o gost_asn1.o gost_crypt.o gost_ctl.o gost_eng.o gosthash.o gost_keywrap.o gost_md.o gost_params.o gost_pmeth.o gost_sign.o
+
+SRC=$(LIBSRC)
+
+LIBNAME=gost
+
+top: 
+       (cd $(TOP); $(MAKE) DIRS=engines EDIRS=$(DIR) sub_all)
+
+all: lib
+
+tags:
+       ctags $(SRC)
+
+errors:
+       $(PERL) ../../util/mkerr.pl -conf gost.ec -nostatic -write $(SRC)
+
+lib: $(LIBOBJ)
+       if [ -n "$(SHARED_LIBS)" ]; then \
+               $(MAKE) -f $(TOP)/Makefile.shared -e \
+                       LIBNAME=$(LIBNAME) \
+                       LIBEXTRAS='$(LIBOBJ)' \
+                       LIBDEPS='-L$(TOP) -lcrypto' \
+                       link_o.$(SHLIB_TARGET); \
+       else \
+               $(AR) $(LIB) $(LIBOBJ); \
+       fi
+       @touch lib
+
+install:
+       [ -n "$(INSTALLTOP)" ] # should be set by top Makefile...
+       if [ -n "$(SHARED_LIBS)" ]; then \
+               set -e; \
+               echo installing $(LIBNAME); \
+               pfx=lib; \
+               if expr "$(PLATFORM)" : "Cygwin" >/dev/null; then \
+                       sfx=".so"; \
+                       cp cyg$(LIBNAME).dll $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/engines/$${pfx}$(LIBNAME)$$sfx.new; \
+               else \
+                       case "$(CFLAGS)" in \
+                       *DSO_BEOS*) sfx=".so";; \
+                       *DSO_DLFCN*) sfx=`expr "$(SHLIB_EXT)" : '.*\(\.[a-z][a-z]*\)' \| ".so"`;; \
+                       *DSO_DL*) sfx=".sl";; \
+                       *DSO_WIN32*) sfx="eay32.dll"; pfx=;; \
+                       *) sfx=".bad";; \
+                       esac; \
+                       cp $${pfx}$(LIBNAME)$$sfx $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/engines/$${pfx}$(LIBNAME)$$sfx.new; \
+               fi; \
+               chmod 555 $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/engines/$${pfx}$(LIBNAME)$$sfx.new; \
+               mv -f $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/engines/$${pfx}$(LIBNAME)$$sfx.new $(INSTALL_PREFIX)$(INSTALLTOP)/$(LIBDIR)/engines/$${pfx}$(LIBNAME)$$sfx; \
+       fi
+
+links:
+
+tests:
+
+update: local_depend
+       @if [ -z "$(THIS)" ]; then $(MAKE) -f $(TOP)/Makefile reflect THIS=$@; fi
+
+depend: local_depend
+       @if [ -z "$(THIS)" ]; then $(MAKE) -f $(TOP)/Makefile reflect THIS=$@; fi
+local_depend:
+       @[ -z "$(THIS)" ] || $(MAKEDEPEND) -- $(CFLAG) $(INCLUDES) $(DEPFLAG) -- $(PROGS) $(LIBSRC)
+
+files:
+       $(PERL) $(TOP)/util/files.pl Makefile >> $(TOP)/MINFO
+
+lint:
+       lint -DLINT $(INCLUDES) $(SRC)>fluff
+
+dclean:
+       $(PERL) -pe 'if (/^# DO NOT DELETE THIS LINE/) {print; exit(0);}' $(MAKEFILE) >Makefile.new
+       mv -f Makefile.new $(MAKEFILE)
+
+clean:
+       rm -f *.o *.obj lib tags core .pure .nfs* *.old *.bak fluff *.so *.sl *.dll
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
+
+gost2001.o: ../../include/openssl/asn1.h ../../include/openssl/asn1t.h
+gost2001.o: ../../include/openssl/bio.h ../../include/openssl/bn.h
+gost2001.o: ../../include/openssl/buffer.h ../../include/openssl/crypto.h
+gost2001.o: ../../include/openssl/dsa.h ../../include/openssl/e_os2.h
+gost2001.o: ../../include/openssl/ec.h ../../include/openssl/ecdh.h
+gost2001.o: ../../include/openssl/ecdsa.h ../../include/openssl/engine.h
+gost2001.o: ../../include/openssl/err.h ../../include/openssl/evp.h
+gost2001.o: ../../include/openssl/lhash.h ../../include/openssl/obj_mac.h
+gost2001.o: ../../include/openssl/objects.h ../../include/openssl/opensslconf.h
+gost2001.o: ../../include/openssl/opensslv.h ../../include/openssl/ossl_typ.h
+gost2001.o: ../../include/openssl/pkcs7.h ../../include/openssl/rand.h
+gost2001.o: ../../include/openssl/safestack.h ../../include/openssl/sha.h
+gost2001.o: ../../include/openssl/stack.h ../../include/openssl/symhacks.h
+gost2001.o: ../../include/openssl/x509.h ../../include/openssl/x509_vfy.h
+gost2001.o: e_gost_err.h gost2001.c gost89.h gost_lcl.h gost_params.h
+gost2001.o: gosthash.h
+gost2001_keyx.o: ../../include/openssl/asn1.h ../../include/openssl/asn1t.h
+gost2001_keyx.o: ../../include/openssl/bio.h ../../include/openssl/bn.h
+gost2001_keyx.o: ../../include/openssl/buffer.h ../../include/openssl/crypto.h
+gost2001_keyx.o: ../../include/openssl/dsa.h ../../include/openssl/e_os2.h
+gost2001_keyx.o: ../../include/openssl/ec.h ../../include/openssl/ecdh.h
+gost2001_keyx.o: ../../include/openssl/ecdsa.h ../../include/openssl/engine.h
+gost2001_keyx.o: ../../include/openssl/evp.h ../../include/openssl/lhash.h
+gost2001_keyx.o: ../../include/openssl/obj_mac.h
+gost2001_keyx.o: ../../include/openssl/objects.h
+gost2001_keyx.o: ../../include/openssl/opensslconf.h
+gost2001_keyx.o: ../../include/openssl/opensslv.h
+gost2001_keyx.o: ../../include/openssl/ossl_typ.h ../../include/openssl/pkcs7.h
+gost2001_keyx.o: ../../include/openssl/rand.h ../../include/openssl/safestack.h
+gost2001_keyx.o: ../../include/openssl/sha.h ../../include/openssl/stack.h
+gost2001_keyx.o: ../../include/openssl/symhacks.h ../../include/openssl/x509.h
+gost2001_keyx.o: ../../include/openssl/x509_vfy.h e_gost_err.h gost2001_keyx.c
+gost2001_keyx.o: gost2001_keyx.h gost89.h gost_keywrap.h gost_lcl.h gosthash.h
+gost89.o: gost89.c gost89.h
+gost94_keyx.o: ../../include/openssl/asn1.h ../../include/openssl/asn1t.h
+gost94_keyx.o: ../../include/openssl/bio.h ../../include/openssl/bn.h
+gost94_keyx.o: ../../include/openssl/buffer.h ../../include/openssl/crypto.h
+gost94_keyx.o: ../../include/openssl/dh.h ../../include/openssl/dsa.h
+gost94_keyx.o: ../../include/openssl/e_os2.h ../../include/openssl/ec.h
+gost94_keyx.o: ../../include/openssl/ecdh.h ../../include/openssl/ecdsa.h
+gost94_keyx.o: ../../include/openssl/engine.h ../../include/openssl/evp.h
+gost94_keyx.o: ../../include/openssl/lhash.h ../../include/openssl/obj_mac.h
+gost94_keyx.o: ../../include/openssl/objects.h
+gost94_keyx.o: ../../include/openssl/opensslconf.h
+gost94_keyx.o: ../../include/openssl/opensslv.h
+gost94_keyx.o: ../../include/openssl/ossl_typ.h ../../include/openssl/pkcs7.h
+gost94_keyx.o: ../../include/openssl/rand.h ../../include/openssl/safestack.h
+gost94_keyx.o: ../../include/openssl/sha.h ../../include/openssl/stack.h
+gost94_keyx.o: ../../include/openssl/symhacks.h ../../include/openssl/x509.h
+gost94_keyx.o: ../../include/openssl/x509_vfy.h e_gost_err.h gost89.h
+gost94_keyx.o: gost94_keyx.c gost_keywrap.h gost_lcl.h gosthash.h
+gost_ameth.o: ../../include/openssl/asn1.h ../../include/openssl/asn1t.h
+gost_ameth.o: ../../include/openssl/bio.h ../../include/openssl/bn.h
+gost_ameth.o: ../../include/openssl/buffer.h ../../include/openssl/cms.h
+gost_ameth.o: ../../include/openssl/crypto.h ../../include/openssl/dsa.h
+gost_ameth.o: ../../include/openssl/e_os2.h ../../include/openssl/ec.h
+gost_ameth.o: ../../include/openssl/ecdh.h ../../include/openssl/ecdsa.h
+gost_ameth.o: ../../include/openssl/engine.h ../../include/openssl/err.h
+gost_ameth.o: ../../include/openssl/evp.h ../../include/openssl/lhash.h
+gost_ameth.o: ../../include/openssl/obj_mac.h ../../include/openssl/objects.h
+gost_ameth.o: ../../include/openssl/opensslconf.h
+gost_ameth.o: ../../include/openssl/opensslv.h ../../include/openssl/ossl_typ.h
+gost_ameth.o: ../../include/openssl/pkcs7.h ../../include/openssl/safestack.h
+gost_ameth.o: ../../include/openssl/sha.h ../../include/openssl/stack.h
+gost_ameth.o: ../../include/openssl/symhacks.h ../../include/openssl/x509.h
+gost_ameth.o: ../../include/openssl/x509_vfy.h e_gost_err.h gost89.h
+gost_ameth.o: gost_ameth.c gost_lcl.h gost_params.h gosthash.h
+gost_asn1.o: ../../include/openssl/asn1.h ../../include/openssl/asn1t.h
+gost_asn1.o: ../../include/openssl/bio.h ../../include/openssl/bn.h
+gost_asn1.o: ../../include/openssl/buffer.h ../../include/openssl/crypto.h
+gost_asn1.o: ../../include/openssl/dsa.h ../../include/openssl/e_os2.h
+gost_asn1.o: ../../include/openssl/ec.h ../../include/openssl/ecdh.h
+gost_asn1.o: ../../include/openssl/ecdsa.h ../../include/openssl/engine.h
+gost_asn1.o: ../../include/openssl/evp.h ../../include/openssl/lhash.h
+gost_asn1.o: ../../include/openssl/obj_mac.h ../../include/openssl/objects.h
+gost_asn1.o: ../../include/openssl/opensslconf.h
+gost_asn1.o: ../../include/openssl/opensslv.h ../../include/openssl/ossl_typ.h
+gost_asn1.o: ../../include/openssl/pkcs7.h ../../include/openssl/safestack.h
+gost_asn1.o: ../../include/openssl/sha.h ../../include/openssl/stack.h
+gost_asn1.o: ../../include/openssl/symhacks.h ../../include/openssl/x509.h
+gost_asn1.o: ../../include/openssl/x509_vfy.h gost89.h gost_asn1.c gost_lcl.h
+gost_asn1.o: gosthash.h
+gost_crypt.o: ../../include/openssl/asn1.h ../../include/openssl/asn1t.h
+gost_crypt.o: ../../include/openssl/bio.h ../../include/openssl/bn.h
+gost_crypt.o: ../../include/openssl/buffer.h ../../include/openssl/crypto.h
+gost_crypt.o: ../../include/openssl/dsa.h ../../include/openssl/e_os2.h
+gost_crypt.o: ../../include/openssl/ec.h ../../include/openssl/ecdh.h
+gost_crypt.o: ../../include/openssl/ecdsa.h ../../include/openssl/engine.h
+gost_crypt.o: ../../include/openssl/evp.h ../../include/openssl/lhash.h
+gost_crypt.o: ../../include/openssl/obj_mac.h ../../include/openssl/objects.h
+gost_crypt.o: ../../include/openssl/opensslconf.h
+gost_crypt.o: ../../include/openssl/opensslv.h ../../include/openssl/ossl_typ.h
+gost_crypt.o: ../../include/openssl/pkcs7.h ../../include/openssl/rand.h
+gost_crypt.o: ../../include/openssl/safestack.h ../../include/openssl/sha.h
+gost_crypt.o: ../../include/openssl/stack.h ../../include/openssl/symhacks.h
+gost_crypt.o: ../../include/openssl/x509.h ../../include/openssl/x509_vfy.h
+gost_crypt.o: e_gost_err.h gost89.h gost_crypt.c gost_lcl.h gosthash.h
+gost_ctl.o: ../../include/openssl/asn1.h ../../include/openssl/asn1t.h
+gost_ctl.o: ../../include/openssl/bio.h ../../include/openssl/bn.h
+gost_ctl.o: ../../include/openssl/buffer.h ../../include/openssl/crypto.h
+gost_ctl.o: ../../include/openssl/dsa.h ../../include/openssl/e_os2.h
+gost_ctl.o: ../../include/openssl/ec.h ../../include/openssl/ecdh.h
+gost_ctl.o: ../../include/openssl/ecdsa.h ../../include/openssl/engine.h
+gost_ctl.o: ../../include/openssl/err.h ../../include/openssl/evp.h
+gost_ctl.o: ../../include/openssl/lhash.h ../../include/openssl/obj_mac.h
+gost_ctl.o: ../../include/openssl/objects.h ../../include/openssl/opensslconf.h
+gost_ctl.o: ../../include/openssl/opensslv.h ../../include/openssl/ossl_typ.h
+gost_ctl.o: ../../include/openssl/pkcs7.h ../../include/openssl/safestack.h
+gost_ctl.o: ../../include/openssl/sha.h ../../include/openssl/stack.h
+gost_ctl.o: ../../include/openssl/symhacks.h ../../include/openssl/x509.h
+gost_ctl.o: ../../include/openssl/x509_vfy.h gost89.h gost_ctl.c gost_lcl.h
+gost_ctl.o: gosthash.h
+gost_eng.o: ../../include/openssl/asn1.h ../../include/openssl/asn1t.h
+gost_eng.o: ../../include/openssl/bio.h ../../include/openssl/bn.h
+gost_eng.o: ../../include/openssl/buffer.h ../../include/openssl/crypto.h
+gost_eng.o: ../../include/openssl/dsa.h ../../include/openssl/e_os2.h
+gost_eng.o: ../../include/openssl/ec.h ../../include/openssl/ecdh.h
+gost_eng.o: ../../include/openssl/ecdsa.h ../../include/openssl/engine.h
+gost_eng.o: ../../include/openssl/err.h ../../include/openssl/evp.h
+gost_eng.o: ../../include/openssl/lhash.h ../../include/openssl/obj_mac.h
+gost_eng.o: ../../include/openssl/objects.h ../../include/openssl/opensslconf.h
+gost_eng.o: ../../include/openssl/opensslv.h ../../include/openssl/ossl_typ.h
+gost_eng.o: ../../include/openssl/pkcs7.h ../../include/openssl/safestack.h
+gost_eng.o: ../../include/openssl/sha.h ../../include/openssl/stack.h
+gost_eng.o: ../../include/openssl/symhacks.h ../../include/openssl/x509.h
+gost_eng.o: ../../include/openssl/x509_vfy.h e_gost_err.h gost89.h gost_eng.c
+gost_eng.o: gost_lcl.h gosthash.h
+gost_keywrap.o: gost89.h gost_keywrap.c gost_keywrap.h
+gost_md.o: ../../include/openssl/asn1.h ../../include/openssl/asn1t.h
+gost_md.o: ../../include/openssl/bio.h ../../include/openssl/bn.h
+gost_md.o: ../../include/openssl/buffer.h ../../include/openssl/crypto.h
+gost_md.o: ../../include/openssl/dsa.h ../../include/openssl/e_os2.h
+gost_md.o: ../../include/openssl/ec.h ../../include/openssl/ecdh.h
+gost_md.o: ../../include/openssl/ecdsa.h ../../include/openssl/engine.h
+gost_md.o: ../../include/openssl/evp.h ../../include/openssl/lhash.h
+gost_md.o: ../../include/openssl/obj_mac.h ../../include/openssl/objects.h
+gost_md.o: ../../include/openssl/opensslconf.h ../../include/openssl/opensslv.h
+gost_md.o: ../../include/openssl/ossl_typ.h ../../include/openssl/pkcs7.h
+gost_md.o: ../../include/openssl/safestack.h ../../include/openssl/sha.h
+gost_md.o: ../../include/openssl/stack.h ../../include/openssl/symhacks.h
+gost_md.o: ../../include/openssl/x509.h ../../include/openssl/x509_vfy.h
+gost_md.o: e_gost_err.h gost89.h gost_lcl.h gost_md.c gosthash.h
+gost_params.o: ../../include/openssl/asn1.h ../../include/openssl/bio.h
+gost_params.o: ../../include/openssl/crypto.h ../../include/openssl/e_os2.h
+gost_params.o: ../../include/openssl/obj_mac.h ../../include/openssl/objects.h
+gost_params.o: ../../include/openssl/opensslconf.h
+gost_params.o: ../../include/openssl/opensslv.h
+gost_params.o: ../../include/openssl/ossl_typ.h
+gost_params.o: ../../include/openssl/safestack.h ../../include/openssl/stack.h
+gost_params.o: ../../include/openssl/symhacks.h gost_params.c gost_params.h
+gost_pmeth.o: ../../include/openssl/asn1.h ../../include/openssl/asn1t.h
+gost_pmeth.o: ../../include/openssl/bio.h ../../include/openssl/bn.h
+gost_pmeth.o: ../../include/openssl/buffer.h ../../include/openssl/conf.h
+gost_pmeth.o: ../../include/openssl/crypto.h ../../include/openssl/dsa.h
+gost_pmeth.o: ../../include/openssl/e_os2.h ../../include/openssl/ec.h
+gost_pmeth.o: ../../include/openssl/ecdh.h ../../include/openssl/ecdsa.h
+gost_pmeth.o: ../../include/openssl/engine.h ../../include/openssl/evp.h
+gost_pmeth.o: ../../include/openssl/lhash.h ../../include/openssl/obj_mac.h
+gost_pmeth.o: ../../include/openssl/objects.h
+gost_pmeth.o: ../../include/openssl/opensslconf.h
+gost_pmeth.o: ../../include/openssl/opensslv.h ../../include/openssl/ossl_typ.h
+gost_pmeth.o: ../../include/openssl/pkcs7.h ../../include/openssl/safestack.h
+gost_pmeth.o: ../../include/openssl/sha.h ../../include/openssl/stack.h
+gost_pmeth.o: ../../include/openssl/symhacks.h ../../include/openssl/x509.h
+gost_pmeth.o: ../../include/openssl/x509_vfy.h ../../include/openssl/x509v3.h
+gost_pmeth.o: e_gost_err.h gost89.h gost_lcl.h gost_params.h gost_pmeth.c
+gost_pmeth.o: gosthash.h
+gost_sign.o: ../../include/openssl/asn1.h ../../include/openssl/asn1t.h
+gost_sign.o: ../../include/openssl/bio.h ../../include/openssl/bn.h
+gost_sign.o: ../../include/openssl/buffer.h ../../include/openssl/crypto.h
+gost_sign.o: ../../include/openssl/dsa.h ../../include/openssl/e_os2.h
+gost_sign.o: ../../include/openssl/ec.h ../../include/openssl/ecdh.h
+gost_sign.o: ../../include/openssl/ecdsa.h ../../include/openssl/engine.h
+gost_sign.o: ../../include/openssl/err.h ../../include/openssl/evp.h
+gost_sign.o: ../../include/openssl/lhash.h ../../include/openssl/obj_mac.h
+gost_sign.o: ../../include/openssl/objects.h
+gost_sign.o: ../../include/openssl/opensslconf.h
+gost_sign.o: ../../include/openssl/opensslv.h ../../include/openssl/ossl_typ.h
+gost_sign.o: ../../include/openssl/pkcs7.h ../../include/openssl/rand.h
+gost_sign.o: ../../include/openssl/safestack.h ../../include/openssl/sha.h
+gost_sign.o: ../../include/openssl/stack.h ../../include/openssl/symhacks.h
+gost_sign.o: ../../include/openssl/x509.h ../../include/openssl/x509_vfy.h
+gost_sign.o: e_gost_err.h gost89.h gost_lcl.h gost_params.h gost_sign.c
+gost_sign.o: gosthash.h
+gosthash.o: gost89.h gosthash.c gosthash.h
diff --git a/README.gost b/README.gost
new file mode 100644 (file)
index 0000000..c96cccc
--- /dev/null
@@ -0,0 +1,300 @@
+GOST ENGINE
+
+This engine provides implementation of Russian cryptography standard.
+This is also an example of adding new cryptoalgorithms into OpenSSL
+without changing its core. If OpenSSL is compiled with dynamic engine
+support, new algorithms can be added even without recompilation of
+OpenSSL and applications which use it.
+
+ALGORITHMS SUPPORTED
+
+GOST R 34.10-94 and GOST R 34.10-2001 - digital signature algorithms.
+   Also support key exchange based on public keys. See RFC 4357 for
+   details of VKO key exchange algorithm. These algorithms use
+   256 bit private keys. Public keys are 1024 bit for 94 and 512 bit for
+   2001 (which is elliptic-curve based). Key exchange algorithms
+   (VKO R 34.10) are supported on these keys too.
+   
+GOST R 34.11-94  Message digest algorithm. 256-bit hash value
+
+GOST 28147-89 - Symmetric cipher  with 256-bit key. Various modes are
+   defined in the standard, but only CFB and CNT modes are implemented
+   in the engine. To make statistical analysis more difficult, key
+   meshing is supported (see RFC 4357).
+
+GOST 28147-89 MAC mode. Message authentication code. While most MAC
+    algorithms  out there are based on hash functions using HMAC
+       algorithm, this algoritm is based on symmetric cipher. 
+       It has 256-bit symmetric key and only 32 bits of MAC value
+       (while HMAC has same key size and value size). 
+
+       It is implemented as combination of EVP_PKEY type and EVP_MD type.
+
+USAGE OF THESE ALGORITHMS
+
+This engine is designed to allow usage of this algorithms in the
+high-level openssl functions, such as PKI, S/MIME and TLS.
+
+See RFC 4490 for S/MIME with GOST algorithms and RFC 4491 for PKI.
+TLS support is implemented according IETF
+draft-chudov-cryptopro-cptls-03.txt and is compatible with
+CryptoPro CSP 3.0 and 3.6 as well as with MagPro CSP. 
+GOST ciphersuites implemented in CryptoPro CSP 2.0 are not supported
+because they use ciphersuite numbers used now by AES ciphersuites.
+
+To use the engine you have to load it via openssl configuration
+file. Applications should read openssl configuration file or provide
+their own means to load engines. Also, applications which operate with
+private keys, should use generic EVP_PKEY API instead of using RSA or
+other algorithm-specific API.
+
+CONFIGURATION FILE
+
+Configuration file should include following statement in the global
+section, i.e. before first bracketed section header (see config(5) for details)
+
+   openssl_conf = openssl_def
+
+where openssl_def is name of the section in configuration file which
+describes global defaults.
+
+This section should contain following statement:
+
+   [openssl_def]
+   engines = engine_section
+
+which points to the section which describes list of the engines to be
+loaded. This section should contain:
+
+       [engine_section]
+       gost = gost_section
+
+And section which describes configuration of the engine should contain
+
+       [gost_section]
+       engine_id = gost
+       dynamic_path = /usr/lib/ssl/engines/libgost.so
+       default_algorithms = ALL
+       CRYPT_PARAMS = id-Gost28147-89-CryptoPro-A-ParamSet
+
+Where engine_id parameter specifies name of engine (should be "gost").
+dynamic_path is a location of the loadable shared library implementing the
+engine. If the engine is compiled statically or is located in the OpenSSL
+engines directory, this line can be omitted. 
+default_algorithms parameter specifies that all algorithms, provided by
+engine, should be used.
+
+The CRYPT_PARAMS parameter is engine-specific. It allows the user to choose
+between different parameter sets of symmetric cipher algorithm. RFC 4357
+specifies several parameters for the GOST 28147-89 algorithm, but OpenSSL
+doesn't provide user interface to choose one when encrypting. So use engine
+configuration parameter instead.
+
+Value of this parameter can be either short name, defined in OpenSSL
+obj_dat.h header file or numeric representation of OID, defined in RFC
+4357. 
+
+USAGE WITH COMMAND LINE openssl UTILITY
+
+1. Generation of private key
+
+       openssl genpkey -algorithm gost2001 -pkeyopt paramset:A -out seckey.pem
+
+  Use -algorithm option to specify algorithm.
+  Use -pkeyopt option to pass paramset to algorithm. The following paramsets
+  are supported by 
+       gost94: 0,A,B,C,D,XA,XB,XC
+       gost2001: 0,A,B,C,XA,XB
+  You can also use numeric representation of OID as to destinate
+  paramset.
+
+  Paramsets starting with X are intended to use for key exchange keys.
+  Paramsets without X are for digital signature keys.
+
+  Paramset for both algorithms 0 is the test paramset which should be used
+  only for test purposes.
+
+There are no algorithm-specific things with generation of certificate
+request once you have a private key.
+
+2. Generation of certificate request along with private/public keypar
+
+   openssl req -newkey gost2001 -pkeyopt paramset:A
+
+   Syntax of -pkeyopt parameter is identical with genpkey command.
+
+   You can also use oldstyle syntax -newkey gost2001:paramfile, but in
+   this case you should create parameter file first. 
+
+   It can be created with
+
+   openssl genpkey -genparam -algorithm gost2001 -pkeyopt paramset:A\
+      -out paramfile.
+
+3. S/MIME operations
+
+If you want to send encrypted mail using GOST algorithms, don't forget
+to specify -gost89 as encryption algorithm for OpenSSL smime command.
+While OpenSSL is clever enough to find out that GOST R 34.11-94 digest
+must be used for digital signing with GOST private key, it have no way
+to derive symmetric encryption algorithm from key exchange keys.
+
+4. TLS operations
+
+OpenSSL supports all four ciphersuites defined in the IETF draft.
+Once you've loaded GOST key and certificate into your TLS server,
+ciphersuites which use GOST 28147-89 encryption are enabled.
+
+Ciphersuites with NULL encryption should be enabled explicitely if
+needed.
+
+GOST2001-GOST89-GOST89 Uses GOST R 34.10-2001 for auth and key exchange
+               GOST 28147-89 for encryption and GOST 28147-89 MAC
+GOST94-GOST89-GOST89 Uses GOST R 34.10-94 for auth and key exchange
+               GOST 28147-89 for encryption and GOST 28147-89 MAC
+GOST2001-NULL-GOST94 Uses GOST R 34.10-2001 for auth and key exchange,
+        no encryption and HMAC, based on GOST R 34.11-94
+GOST94-NULL-GOST94 Uses GOST R 34.10-94 for auth and key exchange,
+        no encryption and HMAC, based on GOST R 34.11-94
+
+Gost 94 and gost 2001 keys can be used simultaneously in the TLS server.
+RSA, DSA and EC keys can be used simultaneously with GOST keys, if
+server implementation supports loading more than two private
+key/certificate pairs. In this case ciphersuites which use any of loaded
+keys would be supported and clients can negotiate ones they wish.
+
+This allows creation of TLS servers which use GOST ciphersuites for
+Russian clients and RSA/DSA ciphersuites for foreign clients.
+
+5. Calculation of digests and symmetric encryption
+ OpenSSL provides specific commands (like sha1, aes etc) for calculation
+ of digests and symmetric encryption. Since such commands cannot be
+ added dynamically, no such commands are provided for GOST algorithms.
+ Use generic commands 'dgst' and 'enc'.
+
+ Calculation of GOST R 34.11-94 message digest
+
+ openssl dgst -md_gost94 datafile
+
+ Note that GOST R 34.11-94 specifies that digest value should be
+ interpreted as little-endian number, but OpenSSL outputs just hex dump
+ of digest value.
+
+ So, to obtain correct digest value, such as produced by gostsum utility
+ included in the engine distribution, bytes of output should be
+ reversed.
+ Calculation of HMAC based on GOST R 34.11-94
+
+ openssl dgst -md_gost94 -mac hmac -macopt key:<32 bytes of key> datafile
+  
+  (or use hexkey if key contain NUL bytes)
+ Calculation of GOST 28147 MAC
+
+ openssl dgst -mac gost-mac -macopt key:<32 bytes of key> datafile
+
+ Note absense of an option that specifies digest algorithm. gost-mac
+ algorithm supports only one digest (which is actually part of
+ implementation of this mac) and OpenSSL is clever enough to find out
+ this.
+
+ Encryption with GOST 28147 CFB mode
+ openssl enc -gost89 -out encrypted-file -in plain-text-file -k <passphrase>  
+ Encryption with GOST 28147 CNT mode
+ openssl enc -gost89-cnt -out encrypted-file -in plain-text-file -k <passphrase>
+
+
+6. Encrypting private keys and PKCS12
+
+To produce PKCS12 files compatible with MagPro CSP, you need to use
+GOST algorithm for encryption of PKCS12 file and also GOST R 34.11-94
+hash to derive key from password.
+
+openssl pksc12 -export -inkey gost.pem -in gost_cert.pem -keypbe gost89\
+   -certpbe gost89 -macalg md_gost94
+7. Testing speed of symmetric ciphers.
+   
+To test performance of GOST symmetric ciphers you should use -evp switch
+of the openssl speed command. Engine-provided ciphers couldn't be
+accessed by cipher-specific functions, only via generic evp interface
+
+ openssl speed -evp gost89
+ openssl speed -evp gost89-cnt
+
+
+PROGRAMMING INTERFACES DETAILS
+
+Applications never should access engine directly. They only use provided
+EVP_PKEY API. But there are some details, which should be taken into
+account.
+
+EVP provides two kinds of API for key exchange:
+
+1. EVP_PKEY_encrypt/EVP_PKEY_decrypt functions, intended to use with
+       RSA-like public key encryption algorithms
+
+2. EVP_PKEY_derive, intended to use with Diffie-Hellman-like shared key
+computing algorithms.
+
+Although VKO R 34.10 algorithms, described in the RFC 4357 are
+definitely second case, engine provides BOTH API for GOST R 34.10 keys.
+
+EVP_PKEY_derive just invokes appropriate VKO algorithm and computes
+256 bit shared key. VKO R 34.10-2001 requires 64 bits of random user key
+material (UKM). This UKM should be transmitted to other party, so it is
+not generated inside derive function.
+
+It should be set by EVP_PKEY_CTX_ctrl function using
+EVP_PKEY_CTRL_SET_IV command after call of EVP_PKEY_derive_init, but
+before EVP_PKEY_derive.
+       unsigned char ukm[8];
+       RAND_bytes(ukm,8);
+   EVP_PKEY_CTX_ctrl(ctx, -1, EVP_PKEY_OP_DERIVE, 8, ukm)
+
+EVP_PKEY_encrypt encrypts provided session key with VKO shared key and
+packs it into GOST key transport structure, described in the RFC 4490.
+
+It typically uses ephemeral key pair to compute shared key and packs its
+public part along with encrypted key. So, for most cases use of 
+EVP_PKEY_encrypt/EVP_PKEY_decrypt with GOST keys is almost same as with
+RSA.
+
+However, if peerkey field in the EVP_PKEY_CTX structure is set (using
+EVP_PKEY_derive_set_peerkey function) to EVP_PKEY structure which has private
+key and uses same parameters as the public key from which this EVP_PKEY_CTX is
+created, EVP_PKEY_encrypt will use this private key to compute shared key and
+set ephemeral key in the GOST_key_transport structure to NULL. In this case
+pkey and peerkey fields in the EVP_PKEY_CTX are used upside-down.
+
+If EVP_PKEY_decrypt encounters GOST_key_transport structure with NULL
+public key field, it tries to use peerkey field from the context to
+compute shared key. In this case peerkey field should really contain
+peer public key.
+
+Encrypt operation supports EVP_PKEY_CTRL_SET_IV operation as well.
+It can be used when some specific restriction on UKM are imposed by
+higher level protocol. For instance, description of GOST ciphersuites
+requires UKM to be derived from shared secret. 
+
+If UKM is not set by this control command, encrypt operation would
+generate random UKM.
+
+
+This sources include implementation of GOST 28147-89 and GOST R 34.11-94
+which are completely indepentent from OpenSSL and can be used separately
+(files gost89.c, gost89.h, gosthash.c, gosthash.h) Utility gostsum (file
+gostsum.c) is provided as example of such separate usage. This is
+program, simular to md5sum and sha1sum utilities, but calculates GOST R
+34.11-94 hash.
+
+Makefile doesn't include rule for compiling gostsum.
+Use command
+
+$(CC) -o gostsum gostsum.c gost89.c gosthash.c
+where $(CC) is name of your C compiler.
+
+Implementations of GOST R 34.10-xx, including VKO algorithms heavily
+depends on OpenSSL BIGNUM and Elliptic Curve libraries.
+
+
diff --git a/e_gost_err.c b/e_gost_err.c
new file mode 100644 (file)
index 0000000..80ef58f
--- /dev/null
@@ -0,0 +1,221 @@
+/* e_gost_err.c */
+/* ====================================================================
+ * Copyright (c) 1999-2015 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+/*
+ * NOTE: this file was auto generated by the mkerr.pl script: any changes
+ * made to it will be overwritten when the script next updates this file,
+ * only reason strings will be preserved.
+ */
+
+#include <stdio.h>
+#include <openssl/err.h>
+#include "e_gost_err.h"
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(0,func,0)
+# define ERR_REASON(reason) ERR_PACK(0,0,reason)
+
+static ERR_STRING_DATA GOST_str_functs[] = {
+    {ERR_FUNC(GOST_F_DECODE_GOST_ALGOR_PARAMS), "DECODE_GOST_ALGOR_PARAMS"},
+    {ERR_FUNC(GOST_F_ENCODE_GOST_ALGOR_PARAMS), "ENCODE_GOST_ALGOR_PARAMS"},
+    {ERR_FUNC(GOST_F_FILL_GOST2001_PARAMS), "FILL_GOST2001_PARAMS"},
+    {ERR_FUNC(GOST_F_FILL_GOST94_PARAMS), "FILL_GOST94_PARAMS"},
+    {ERR_FUNC(GOST_F_GET_ENCRYPTION_PARAMS), "GET_ENCRYPTION_PARAMS"},
+    {ERR_FUNC(GOST_F_GOST2001_COMPUTE_PUBLIC), "GOST2001_COMPUTE_PUBLIC"},
+    {ERR_FUNC(GOST_F_GOST2001_DO_SIGN), "GOST2001_DO_SIGN"},
+    {ERR_FUNC(GOST_F_GOST2001_DO_VERIFY), "GOST2001_DO_VERIFY"},
+    {ERR_FUNC(GOST_F_GOST2001_KEYGEN), "GOST2001_KEYGEN"},
+    {ERR_FUNC(GOST_F_GOST89_GET_ASN1_PARAMETERS),
+     "GOST89_GET_ASN1_PARAMETERS"},
+    {ERR_FUNC(GOST_F_GOST89_SET_ASN1_PARAMETERS),
+     "GOST89_SET_ASN1_PARAMETERS"},
+    {ERR_FUNC(GOST_F_GOST94_COMPUTE_PUBLIC), "GOST94_COMPUTE_PUBLIC"},
+    {ERR_FUNC(GOST_F_GOST_CIPHER_CTL), "GOST_CIPHER_CTL"},
+    {ERR_FUNC(GOST_F_GOST_DO_SIGN), "GOST_DO_SIGN"},
+    {ERR_FUNC(GOST_F_GOST_DO_VERIFY), "GOST_DO_VERIFY"},
+    {ERR_FUNC(GOST_F_GOST_IMIT_CTRL), "GOST_IMIT_CTRL"},
+    {ERR_FUNC(GOST_F_GOST_IMIT_FINAL), "GOST_IMIT_FINAL"},
+    {ERR_FUNC(GOST_F_GOST_IMIT_UPDATE), "GOST_IMIT_UPDATE"},
+    {ERR_FUNC(GOST_F_GOST_SIGN_KEYGEN), "GOST_SIGN_KEYGEN"},
+    {ERR_FUNC(GOST_F_PARAM_COPY_GOST01), "PARAM_COPY_GOST01"},
+    {ERR_FUNC(GOST_F_PARAM_COPY_GOST94), "PARAM_COPY_GOST94"},
+    {ERR_FUNC(GOST_F_PKEY_GOST01CP_DECRYPT), "PKEY_GOST01CP_DECRYPT"},
+    {ERR_FUNC(GOST_F_PKEY_GOST01CP_ENCRYPT), "PKEY_GOST01CP_ENCRYPT"},
+    {ERR_FUNC(GOST_F_PKEY_GOST01CP_KEYGEN), "PKEY_GOST01CP_KEYGEN"},
+    {ERR_FUNC(GOST_F_PKEY_GOST01_PARAMGEN), "PKEY_GOST01_PARAMGEN"},
+    {ERR_FUNC(GOST_F_PKEY_GOST2001_DERIVE), "PKEY_GOST2001_DERIVE"},
+    {ERR_FUNC(GOST_F_PKEY_GOST94CP_DECRYPT), "PKEY_GOST94CP_DECRYPT"},
+    {ERR_FUNC(GOST_F_PKEY_GOST94CP_ENCRYPT), "PKEY_GOST94CP_ENCRYPT"},
+    {ERR_FUNC(GOST_F_PKEY_GOST94CP_KEYGEN), "PKEY_GOST94CP_KEYGEN"},
+    {ERR_FUNC(GOST_F_PKEY_GOST94_PARAMGEN), "PKEY_GOST94_PARAMGEN"},
+    {ERR_FUNC(GOST_F_PKEY_GOST_CTRL), "PKEY_GOST_CTRL"},
+    {ERR_FUNC(GOST_F_PKEY_GOST_CTRL01_STR), "PKEY_GOST_CTRL01_STR"},
+    {ERR_FUNC(GOST_F_PKEY_GOST_CTRL94_STR), "PKEY_GOST_CTRL94_STR"},
+    {ERR_FUNC(GOST_F_PKEY_GOST_MAC_CTRL), "PKEY_GOST_MAC_CTRL"},
+    {ERR_FUNC(GOST_F_PKEY_GOST_MAC_CTRL_STR), "PKEY_GOST_MAC_CTRL_STR"},
+    {ERR_FUNC(GOST_F_PKEY_GOST_MAC_KEYGEN), "PKEY_GOST_MAC_KEYGEN"},
+    {ERR_FUNC(GOST_F_PRINT_GOST_01), "PRINT_GOST_01"},
+    {ERR_FUNC(GOST_F_PRIV_DECODE_GOST), "PRIV_DECODE_GOST"},
+    {ERR_FUNC(GOST_F_PUB_DECODE_GOST01), "PUB_DECODE_GOST01"},
+    {ERR_FUNC(GOST_F_PUB_DECODE_GOST94), "PUB_DECODE_GOST94"},
+    {ERR_FUNC(GOST_F_PUB_ENCODE_GOST01), "PUB_ENCODE_GOST01"},
+    {ERR_FUNC(GOST_F_UNPACK_CC_SIGNATURE), "UNPACK_CC_SIGNATURE"},
+    {ERR_FUNC(GOST_F_UNPACK_CP_SIGNATURE), "UNPACK_CP_SIGNATURE"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA GOST_str_reasons[] = {
+    {ERR_REASON(GOST_R_BAD_KEY_PARAMETERS_FORMAT),
+     "bad key parameters format"},
+    {ERR_REASON(GOST_R_BAD_PKEY_PARAMETERS_FORMAT),
+     "bad pkey parameters format"},
+    {ERR_REASON(GOST_R_CANNOT_PACK_EPHEMERAL_KEY),
+     "cannot pack ephemeral key"},
+    {ERR_REASON(GOST_R_CTRL_CALL_FAILED), "ctrl call failed"},
+    {ERR_REASON(GOST_R_ERROR_COMPUTING_SHARED_KEY),
+     "error computing shared key"},
+    {ERR_REASON(GOST_R_ERROR_PACKING_KEY_TRANSPORT_INFO),
+     "error packing key transport info"},
+    {ERR_REASON(GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO),
+     "error parsing key transport info"},
+    {ERR_REASON(GOST_R_INCOMPATIBLE_ALGORITHMS), "incompatible algorithms"},
+    {ERR_REASON(GOST_R_INCOMPATIBLE_PEER_KEY), "incompatible peer key"},
+    {ERR_REASON(GOST_R_INVALID_CIPHER_PARAMS), "invalid cipher params"},
+    {ERR_REASON(GOST_R_INVALID_CIPHER_PARAM_OID), "invalid cipher param oid"},
+    {ERR_REASON(GOST_R_INVALID_DIGEST_TYPE), "invalid digest type"},
+    {ERR_REASON(GOST_R_INVALID_GOST94_PARMSET), "invalid gost94 parmset"},
+    {ERR_REASON(GOST_R_INVALID_IV_LENGTH), "invalid iv length"},
+    {ERR_REASON(GOST_R_INVALID_MAC_KEY_LENGTH), "invalid mac key length"},
+    {ERR_REASON(GOST_R_INVALID_PARAMSET), "invalid paramset"},
+    {ERR_REASON(GOST_R_KEY_IS_NOT_INITALIZED), "key is not initalized"},
+    {ERR_REASON(GOST_R_KEY_IS_NOT_INITIALIZED), "key is not initialized"},
+    {ERR_REASON(GOST_R_KEY_PARAMETERS_MISSING), "key parameters missing"},
+    {ERR_REASON(GOST_R_MAC_KEY_NOT_SET), "mac key not set"},
+    {ERR_REASON(GOST_R_MALLOC_FAILURE), "malloc failure"},
+    {ERR_REASON(GOST_R_NO_MEMORY), "no memory"},
+    {ERR_REASON(GOST_R_NO_PARAMETERS_SET), "no parameters set"},
+    {ERR_REASON(GOST_R_NO_PEER_KEY), "no peer key"},
+    {ERR_REASON(GOST_R_NO_PRIVATE_PART_OF_NON_EPHEMERAL_KEYPAIR),
+     "no private part of non ephemeral keypair"},
+    {ERR_REASON(GOST_R_PUBLIC_KEY_UNDEFINED), "public key undefined"},
+    {ERR_REASON(GOST_R_RANDOM_GENERATOR_ERROR), "random generator error"},
+    {ERR_REASON(GOST_R_RANDOM_GENERATOR_FAILURE), "random generator failure"},
+    {ERR_REASON(GOST_R_RANDOM_NUMBER_GENERATOR_FAILED),
+     "random number generator failed"},
+    {ERR_REASON(GOST_R_SIGNATURE_MISMATCH), "signature mismatch"},
+    {ERR_REASON(GOST_R_SIGNATURE_PARTS_GREATER_THAN_Q),
+     "signature parts greater than q"},
+    {ERR_REASON(GOST_R_UKM_NOT_SET), "ukm not set"},
+    {ERR_REASON(GOST_R_UNSUPPORTED_CIPHER_CTL_COMMAND),
+     "unsupported cipher ctl command"},
+    {ERR_REASON(GOST_R_UNSUPPORTED_PARAMETER_SET),
+     "unsupported parameter set"},
+    {0, NULL}
+};
+
+#endif
+
+#ifdef GOST_LIB_NAME
+static ERR_STRING_DATA GOST_lib_name[] = {
+    {0, GOST_LIB_NAME},
+    {0, NULL}
+};
+#endif
+
+static int GOST_lib_error_code = 0;
+static int GOST_error_init = 1;
+
+void ERR_load_GOST_strings(void)
+{
+    if (GOST_lib_error_code == 0)
+        GOST_lib_error_code = ERR_get_next_error_library();
+
+    if (GOST_error_init) {
+        GOST_error_init = 0;
+#ifndef OPENSSL_NO_ERR
+        ERR_load_strings(GOST_lib_error_code, GOST_str_functs);
+        ERR_load_strings(GOST_lib_error_code, GOST_str_reasons);
+#endif
+
+#ifdef GOST_LIB_NAME
+        GOST_lib_name->error = ERR_PACK(GOST_lib_error_code, 0, 0);
+        ERR_load_strings(0, GOST_lib_name);
+#endif
+    }
+}
+
+void ERR_unload_GOST_strings(void)
+{
+    if (GOST_error_init == 0) {
+#ifndef OPENSSL_NO_ERR
+        ERR_unload_strings(GOST_lib_error_code, GOST_str_functs);
+        ERR_unload_strings(GOST_lib_error_code, GOST_str_reasons);
+#endif
+
+#ifdef GOST_LIB_NAME
+        ERR_unload_strings(0, GOST_lib_name);
+#endif
+        GOST_error_init = 1;
+    }
+}
+
+void ERR_GOST_error(int function, int reason, char *file, int line)
+{
+    if (GOST_lib_error_code == 0)
+        GOST_lib_error_code = ERR_get_next_error_library();
+    ERR_PUT_error(GOST_lib_error_code, function, reason, file, line);
+}
diff --git a/e_gost_err.h b/e_gost_err.h
new file mode 100644 (file)
index 0000000..a2018ec
--- /dev/null
@@ -0,0 +1,158 @@
+/* ====================================================================
+ * Copyright (c) 2001-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+#ifndef HEADER_GOST_ERR_H
+# define HEADER_GOST_ERR_H
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+/* BEGIN ERROR CODES */
+/*
+ * The following lines are auto generated by the script mkerr.pl. Any changes
+ * made after this point may be overwritten when the script is next run.
+ */
+void ERR_load_GOST_strings(void);
+void ERR_unload_GOST_strings(void);
+void ERR_GOST_error(int function, int reason, char *file, int line);
+# define GOSTerr(f,r) ERR_GOST_error((f),(r),__FILE__,__LINE__)
+
+/* Error codes for the GOST functions. */
+
+/* Function codes. */
+# define GOST_F_DECODE_GOST_ALGOR_PARAMS                  99
+# define GOST_F_ENCODE_GOST_ALGOR_PARAMS                  100
+# define GOST_F_FILL_GOST2001_PARAMS                      101
+# define GOST_F_FILL_GOST94_PARAMS                        102
+# define GOST_F_GET_ENCRYPTION_PARAMS                     103
+# define GOST_F_GOST2001_COMPUTE_PUBLIC                   104
+# define GOST_F_GOST2001_DO_SIGN                          105
+# define GOST_F_GOST2001_DO_VERIFY                        106
+# define GOST_F_GOST2001_KEYGEN                           107
+# define GOST_F_GOST89_GET_ASN1_PARAMETERS                108
+# define GOST_F_GOST89_SET_ASN1_PARAMETERS                109
+# define GOST_F_GOST94_COMPUTE_PUBLIC                     110
+# define GOST_F_GOST_CIPHER_CTL                           111
+# define GOST_F_GOST_DO_SIGN                              112
+# define GOST_F_GOST_DO_VERIFY                            113
+# define GOST_F_GOST_IMIT_CTRL                            114
+# define GOST_F_GOST_IMIT_FINAL                           140
+# define GOST_F_GOST_IMIT_UPDATE                          115
+# define GOST_F_GOST_SIGN_KEYGEN                          142
+# define GOST_F_PARAM_COPY_GOST01                         116
+# define GOST_F_PARAM_COPY_GOST94                         117
+# define GOST_F_PKEY_GOST01CP_DECRYPT                     118
+# define GOST_F_PKEY_GOST01CP_ENCRYPT                     119
+# define GOST_F_PKEY_GOST01CP_KEYGEN                      120
+# define GOST_F_PKEY_GOST01_PARAMGEN                      138
+# define GOST_F_PKEY_GOST2001_DERIVE                      121
+# define GOST_F_PKEY_GOST94CP_DECRYPT                     122
+# define GOST_F_PKEY_GOST94CP_ENCRYPT                     123
+# define GOST_F_PKEY_GOST94CP_KEYGEN                      124
+# define GOST_F_PKEY_GOST94_PARAMGEN                      139
+# define GOST_F_PKEY_GOST_CTRL                            125
+# define GOST_F_PKEY_GOST_CTRL01_STR                      126
+# define GOST_F_PKEY_GOST_CTRL94_STR                      127
+# define GOST_F_PKEY_GOST_MAC_CTRL                        128
+# define GOST_F_PKEY_GOST_MAC_CTRL_STR                    129
+# define GOST_F_PKEY_GOST_MAC_KEYGEN                      130
+# define GOST_F_PRINT_GOST_01                             131
+# define GOST_F_PRIV_DECODE_GOST                          132
+# define GOST_F_PUB_DECODE_GOST01                         133
+# define GOST_F_PUB_DECODE_GOST94                         134
+# define GOST_F_PUB_ENCODE_GOST01                         135
+# define GOST_F_UNPACK_CC_SIGNATURE                       136
+# define GOST_F_UNPACK_CP_SIGNATURE                       137
+
+/* Reason codes. */
+# define GOST_R_BAD_KEY_PARAMETERS_FORMAT                 99
+# define GOST_R_BAD_PKEY_PARAMETERS_FORMAT                100
+# define GOST_R_CANNOT_PACK_EPHEMERAL_KEY                 101
+# define GOST_R_CTRL_CALL_FAILED                          132
+# define GOST_R_ERROR_COMPUTING_SHARED_KEY                102
+# define GOST_R_ERROR_PACKING_KEY_TRANSPORT_INFO          103
+# define GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO          104
+# define GOST_R_INCOMPATIBLE_ALGORITHMS                   105
+# define GOST_R_INCOMPATIBLE_PEER_KEY                     131
+# define GOST_R_INVALID_CIPHER_PARAMS                     106
+# define GOST_R_INVALID_CIPHER_PARAM_OID                  107
+# define GOST_R_INVALID_DIGEST_TYPE                       108
+# define GOST_R_INVALID_GOST94_PARMSET                    109
+# define GOST_R_INVALID_IV_LENGTH                         110
+# define GOST_R_INVALID_MAC_KEY_LENGTH                    111
+# define GOST_R_INVALID_PARAMSET                          112
+# define GOST_R_KEY_IS_NOT_INITALIZED                     113
+# define GOST_R_KEY_IS_NOT_INITIALIZED                    114
+# define GOST_R_KEY_PARAMETERS_MISSING                    115
+# define GOST_R_MAC_KEY_NOT_SET                           116
+# define GOST_R_MALLOC_FAILURE                            117
+# define GOST_R_NO_MEMORY                                 118
+# define GOST_R_NO_PARAMETERS_SET                         119
+# define GOST_R_NO_PEER_KEY                               120
+# define GOST_R_NO_PRIVATE_PART_OF_NON_EPHEMERAL_KEYPAIR  121
+# define GOST_R_PUBLIC_KEY_UNDEFINED                      122
+# define GOST_R_RANDOM_GENERATOR_ERROR                    123
+# define GOST_R_RANDOM_GENERATOR_FAILURE                  124
+# define GOST_R_RANDOM_NUMBER_GENERATOR_FAILED            125
+# define GOST_R_SIGNATURE_MISMATCH                        126
+# define GOST_R_SIGNATURE_PARTS_GREATER_THAN_Q            127
+# define GOST_R_UKM_NOT_SET                               128
+# define GOST_R_UNSUPPORTED_CIPHER_CTL_COMMAND            129
+# define GOST_R_UNSUPPORTED_PARAMETER_SET                 130
+
+#ifdef  __cplusplus
+}
+#endif
+#endif
diff --git a/e_gost_err.proto b/e_gost_err.proto
new file mode 100644 (file)
index 0000000..c57bd1b
--- /dev/null
@@ -0,0 +1,61 @@
+/* ====================================================================
+ * Copyright (c) 2001-2005 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+#ifndef HEADER_GOST_ERR_H
+#define HEADER_GOST_ERR_H
+
+#define GOST_LIB_NAME "GOST engine"
+#ifdef __cplusplus
+ extern "C" {
+#endif
diff --git a/gost.ec b/gost.ec
new file mode 100644 (file)
index 0000000..6c2c85e
--- /dev/null
+++ b/gost.ec
@@ -0,0 +1,5 @@
+L GOST                         e_gost_err.h                    e_gost_err.c
+L NONE                 asymm.h                         NONE
+L NONE                 md.h                            NONE
+L NONE                 crypt.h                         NONE
+L NONE                 gostkeyx.h                      NONE
diff --git a/gost2001.c b/gost2001.c
new file mode 100644 (file)
index 0000000..9536295
--- /dev/null
@@ -0,0 +1,466 @@
+/**********************************************************************
+ *                          gost2001.c                                *
+ *             Copyright (c) 2005-2006 Cryptocom LTD                  *
+ *         This file is distributed under the same license as OpenSSL *
+ *                                                                    *
+ *          Implementation of GOST R 34.10-2001                                   *
+ *          Requires OpenSSL 0.9.9 for compilation                    *
+ **********************************************************************/
+#include "gost_lcl.h"
+#include "gost_params.h"
+#include <string.h>
+#include <openssl/rand.h>
+#include <openssl/ecdsa.h>
+#include <openssl/err.h>
+#include "e_gost_err.h"
+#ifdef DEBUG_SIGN
+extern
+void dump_signature(const char *message, const unsigned char *buffer,
+                    size_t len);
+void dump_dsa_sig(const char *message, DSA_SIG *sig);
+#else
+
+# define dump_signature(a,b,c)
+# define dump_dsa_sig(a,b)
+#endif
+
+/*
+ * Fills EC_KEY structure hidden in the app_data field of DSA structure
+ * with parameter information, extracted from parameter array in
+ * params.c file.
+ *
+ * Also fils DSA->q field with copy of EC_GROUP order field to make
+ * DSA_size function work
+ */
+int fill_GOST2001_params(EC_KEY *eckey, int nid)
+{
+    R3410_2001_params *params = R3410_2001_paramset;
+    EC_GROUP *grp = NULL;
+    BIGNUM *p = NULL, *q = NULL, *a = NULL, *b = NULL, *x = NULL, *y = NULL;
+    EC_POINT *P = NULL;
+    BN_CTX *ctx = BN_CTX_new();
+    int ok = 0;
+
+    if(!ctx) {
+        GOSTerr(GOST_F_FILL_GOST2001_PARAMS, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    BN_CTX_start(ctx);
+    p = BN_CTX_get(ctx);
+    a = BN_CTX_get(ctx);
+    b = BN_CTX_get(ctx);
+    x = BN_CTX_get(ctx);
+    y = BN_CTX_get(ctx);
+    q = BN_CTX_get(ctx);
+    if(!p || !a || !b || !x || !y || !q) {
+        GOSTerr(GOST_F_FILL_GOST2001_PARAMS, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    while (params->nid != NID_undef && params->nid != nid)
+        params++;
+    if (params->nid == NID_undef) {
+        GOSTerr(GOST_F_FILL_GOST2001_PARAMS,
+                GOST_R_UNSUPPORTED_PARAMETER_SET);
+        goto err;
+    }
+    if(!BN_hex2bn(&p, params->p)
+        || !BN_hex2bn(&a, params->a)
+        || !BN_hex2bn(&b, params->b)) {
+        GOSTerr(GOST_F_FILL_GOST2001_PARAMS,
+                ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    grp = EC_GROUP_new_curve_GFp(p, a, b, ctx);
+    if(!grp)  {
+        GOSTerr(GOST_F_FILL_GOST2001_PARAMS, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    P = EC_POINT_new(grp);
+    if(!P)  {
+        GOSTerr(GOST_F_FILL_GOST2001_PARAMS, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if(!BN_hex2bn(&x, params->x)
+        || !BN_hex2bn(&y, params->y)
+        || !EC_POINT_set_affine_coordinates_GFp(grp, P, x, y, ctx)
+        || !BN_hex2bn(&q, params->q))  {
+        GOSTerr(GOST_F_FILL_GOST2001_PARAMS, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+#ifdef DEBUG_KEYS
+    fprintf(stderr, "Set params index %d oid %s\nq=",
+            (params - R3410_2001_paramset), OBJ_nid2sn(params->nid));
+    BN_print_fp(stderr, q);
+    fprintf(stderr, "\n");
+#endif
+
+    if(!EC_GROUP_set_generator(grp, P, q, NULL)) {
+        GOSTerr(GOST_F_FILL_GOST2001_PARAMS, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    EC_GROUP_set_curve_name(grp, params->nid);
+    if(!EC_KEY_set_group(eckey, grp)) {
+        GOSTerr(GOST_F_FILL_GOST2001_PARAMS, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    ok = 1;
+ err:
+    if (P) EC_POINT_free(P);
+    if (grp) EC_GROUP_free(grp);
+    if (ctx) {
+        BN_CTX_end(ctx);
+        BN_CTX_free(ctx);
+    }
+    return ok;
+}
+
+/*
+ * Computes gost2001 signature as DSA_SIG structure
+ *
+ *
+ */
+DSA_SIG *gost2001_do_sign(const unsigned char *dgst, int dlen, EC_KEY *eckey)
+{
+    DSA_SIG *newsig = NULL, *ret = NULL;
+    BIGNUM *md = hashsum2bn(dgst);
+    BIGNUM *order = NULL;
+    const EC_GROUP *group;
+    const BIGNUM *priv_key;
+    BIGNUM *r = NULL, *s = NULL, *X = NULL, *tmp = NULL, *tmp2 = NULL, *k =
+        NULL, *e = NULL;
+    EC_POINT *C = NULL;
+    BN_CTX *ctx = BN_CTX_new();
+    if(!ctx || !md) {
+        GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    BN_CTX_start(ctx);
+    OPENSSL_assert(dlen == 32);
+    newsig = DSA_SIG_new();
+    if (!newsig) {
+        GOSTerr(GOST_F_GOST2001_DO_SIGN, GOST_R_NO_MEMORY);
+        goto err;
+    }
+    group = EC_KEY_get0_group(eckey);
+    if(!group) {
+        GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    order = BN_CTX_get(ctx);
+    if(!order || !EC_GROUP_get_order(group, order, ctx)) {
+        GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    priv_key = EC_KEY_get0_private_key(eckey);
+    if(!priv_key) {
+        GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    e = BN_CTX_get(ctx);
+    if(!e || !BN_mod(e, md, order, ctx)) {
+        GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+#ifdef DEBUG_SIGN
+    fprintf(stderr, "digest as bignum=");
+    BN_print_fp(stderr, md);
+    fprintf(stderr, "\ndigest mod q=");
+    BN_print_fp(stderr, e);
+    fprintf(stderr, "\n");
+#endif
+    if (BN_is_zero(e)) {
+        BN_one(e);
+    }
+    k = BN_CTX_get(ctx);
+    C = EC_POINT_new(group);
+    if(!k || !C) {
+        GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    do {
+        do {
+            if (!BN_rand_range(k, order)) {
+                GOSTerr(GOST_F_GOST2001_DO_SIGN,
+                        GOST_R_RANDOM_NUMBER_GENERATOR_FAILED);
+                goto err;
+            }
+            if (!EC_POINT_mul(group, C, k, NULL, NULL, ctx)) {
+                GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_EC_LIB);
+                goto err;
+            }
+            if (!X)
+                X = BN_CTX_get(ctx);
+            if (!r)
+                r = BN_CTX_get(ctx);
+            if (!X || !r) {
+                GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            if (!EC_POINT_get_affine_coordinates_GFp(group, C, X, NULL, ctx)) {
+                GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_EC_LIB);
+                goto err;
+            }
+
+            if(!BN_nnmod(r, X, order, ctx)) {
+                GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_INTERNAL_ERROR);
+                goto err;
+            }
+        }
+        while (BN_is_zero(r));
+        /* s =  (r*priv_key+k*e) mod order */
+        if (!tmp)
+            tmp = BN_CTX_get(ctx);
+        if (!tmp2)
+            tmp2 = BN_CTX_get(ctx);
+        if (!s)
+            s = BN_CTX_get(ctx);
+        if (!tmp || !tmp2 || !s) {
+            GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
+        if(!BN_mod_mul(tmp, priv_key, r, order, ctx)
+            || !BN_mod_mul(tmp2, k, e, order, ctx)
+            || !BN_mod_add(s, tmp, tmp2, order, ctx)) {
+            GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+    }
+    while (BN_is_zero(s));
+
+    newsig->s = BN_dup(s);
+    newsig->r = BN_dup(r);
+    if(!newsig->s || !newsig->r) {
+        GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    ret = newsig;
+ err:
+    if(ctx) {
+        BN_CTX_end(ctx);
+        BN_CTX_free(ctx);
+    }
+    if (C) EC_POINT_free(C);
+    if (md) BN_free(md);
+    if (!ret && newsig) {
+        DSA_SIG_free(newsig);
+    }
+    return ret;
+}
+
+/*
+ * Verifies gost 2001 signature
+ *
+ */
+int gost2001_do_verify(const unsigned char *dgst, int dgst_len,
+                       DSA_SIG *sig, EC_KEY *ec)
+{
+    BN_CTX *ctx = BN_CTX_new();
+    const EC_GROUP *group = EC_KEY_get0_group(ec);
+    BIGNUM *order;
+    BIGNUM *md = NULL, *e = NULL, *R = NULL, *v = NULL, *z1 = NULL, *z2 =
+        NULL;
+    BIGNUM *X = NULL, *tmp = NULL;
+    EC_POINT *C = NULL;
+    const EC_POINT *pub_key = NULL;
+    int ok = 0;
+
+    if(!ctx || !group) {
+        GOSTerr(GOST_F_GOST2001_DO_VERIFY, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    BN_CTX_start(ctx);
+    order = BN_CTX_get(ctx);
+    e = BN_CTX_get(ctx);
+    z1 = BN_CTX_get(ctx);
+    z2 = BN_CTX_get(ctx);
+    tmp = BN_CTX_get(ctx);
+    X = BN_CTX_get(ctx);
+    R = BN_CTX_get(ctx);
+    v = BN_CTX_get(ctx);
+    if(!order || !e || !z1 || !z2 || !tmp || !X || !R || !v) {
+        GOSTerr(GOST_F_GOST2001_DO_VERIFY, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    pub_key = EC_KEY_get0_public_key(ec);
+    if(!pub_key || !EC_GROUP_get_order(group, order, ctx)) {
+        GOSTerr(GOST_F_GOST2001_DO_VERIFY, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    if (BN_is_zero(sig->s) || BN_is_zero(sig->r) ||
+        (BN_cmp(sig->s, order) >= 1) || (BN_cmp(sig->r, order) >= 1)) {
+        GOSTerr(GOST_F_GOST2001_DO_VERIFY,
+                GOST_R_SIGNATURE_PARTS_GREATER_THAN_Q);
+        goto err;
+
+    }
+    md = hashsum2bn(dgst);
+
+    if(!md || !BN_mod(e, md, order, ctx)) {
+        GOSTerr(GOST_F_GOST2001_DO_VERIFY, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+#ifdef DEBUG_SIGN
+    fprintf(stderr, "digest as bignum: ");
+    BN_print_fp(stderr, md);
+    fprintf(stderr, "\ndigest mod q: ");
+    BN_print_fp(stderr, e);
+#endif
+    if (BN_is_zero(e) && !BN_one(e)) {
+        GOSTerr(GOST_F_GOST2001_DO_VERIFY, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    v = BN_mod_inverse(v, e, order, ctx);
+    if(!v
+        || !BN_mod_mul(z1, sig->s, v, order, ctx)
+        || !BN_sub(tmp, order, sig->r)
+        || !BN_mod_mul(z2, tmp, v, order, ctx)) {
+        GOSTerr(GOST_F_GOST2001_DO_VERIFY, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+#ifdef DEBUG_SIGN
+    fprintf(stderr, "\nInverted digest value: ");
+    BN_print_fp(stderr, v);
+    fprintf(stderr, "\nz1: ");
+    BN_print_fp(stderr, z1);
+    fprintf(stderr, "\nz2: ");
+    BN_print_fp(stderr, z2);
+#endif
+    C = EC_POINT_new(group);
+    if (!C) {
+        GOSTerr(GOST_F_GOST2001_DO_VERIFY, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    if (!EC_POINT_mul(group, C, z1, pub_key, z2, ctx)) {
+        GOSTerr(GOST_F_GOST2001_DO_VERIFY, ERR_R_EC_LIB);
+        goto err;
+    }
+    if (!EC_POINT_get_affine_coordinates_GFp(group, C, X, NULL, ctx)) {
+        GOSTerr(GOST_F_GOST2001_DO_VERIFY, ERR_R_EC_LIB);
+        goto err;
+    }
+    if(!BN_mod(R, X, order, ctx)) {
+        GOSTerr(GOST_F_GOST2001_DO_VERIFY, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+#ifdef DEBUG_SIGN
+    fprintf(stderr, "\nX=");
+    BN_print_fp(stderr, X);
+    fprintf(stderr, "\nX mod q=");
+    BN_print_fp(stderr, R);
+    fprintf(stderr, "\n");
+#endif
+    if (BN_cmp(R, sig->r) != 0) {
+        GOSTerr(GOST_F_GOST2001_DO_VERIFY, GOST_R_SIGNATURE_MISMATCH);
+    } else {
+        ok = 1;
+    }
+ err:
+    if (C) EC_POINT_free(C);
+    if (ctx) {
+        BN_CTX_end(ctx);
+        BN_CTX_free(ctx);
+    }
+    if (md) BN_free(md);
+    return ok;
+}
+
+/*
+ * Computes GOST R 34.10-2001 public key
+ *
+ *
+ */
+int gost2001_compute_public(EC_KEY *ec)
+{
+    const EC_GROUP *group = EC_KEY_get0_group(ec);
+    EC_POINT *pub_key = NULL;
+    const BIGNUM *priv_key = NULL;
+    BN_CTX *ctx = NULL;
+    int ok = 0;
+
+    if (!group) {
+        GOSTerr(GOST_F_GOST2001_COMPUTE_PUBLIC,
+                GOST_R_KEY_IS_NOT_INITIALIZED);
+        return 0;
+    }
+    ctx = BN_CTX_new();
+    if(!ctx) {
+        GOSTerr(GOST_F_GOST2001_COMPUTE_PUBLIC, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    BN_CTX_start(ctx);
+    if (!(priv_key = EC_KEY_get0_private_key(ec))) {
+        GOSTerr(GOST_F_GOST2001_COMPUTE_PUBLIC, ERR_R_EC_LIB);
+        goto err;
+    }
+
+    pub_key = EC_POINT_new(group);
+    if(!pub_key) {
+        GOSTerr(GOST_F_GOST2001_COMPUTE_PUBLIC, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx)) {
+        GOSTerr(GOST_F_GOST2001_COMPUTE_PUBLIC, ERR_R_EC_LIB);
+        goto err;
+    }
+    if (!EC_KEY_set_public_key(ec, pub_key)) {
+        GOSTerr(GOST_F_GOST2001_COMPUTE_PUBLIC, ERR_R_EC_LIB);
+        goto err;
+    }
+    ok = 256;
+ err:
+    if (pub_key) EC_POINT_free(pub_key);
+    if (ctx) {
+        BN_CTX_end(ctx);
+        BN_CTX_free(ctx);
+    }
+    return ok;
+}
+
+/*
+ *
+ * Generates GOST R 34.10-2001 keypair
+ *
+ *
+ */
+int gost2001_keygen(EC_KEY *ec)
+{
+    BIGNUM *order = BN_new(), *d = BN_new();
+    const EC_GROUP *group = EC_KEY_get0_group(ec);
+
+    if(!group || !EC_GROUP_get_order(group, order, NULL)) {
+        GOSTerr(GOST_F_GOST2001_KEYGEN, ERR_R_INTERNAL_ERROR);
+        BN_free(d);
+        BN_free(order);
+        return 0;
+    }
+
+    do {
+        if (!BN_rand_range(d, order)) {
+            GOSTerr(GOST_F_GOST2001_KEYGEN,
+                    GOST_R_RANDOM_NUMBER_GENERATOR_FAILED);
+            BN_free(d);
+            BN_free(order);
+            return 0;
+        }
+    }
+    while (BN_is_zero(d));
+
+    if(!EC_KEY_set_private_key(ec, d)) {
+        GOSTerr(GOST_F_GOST2001_KEYGEN, ERR_R_INTERNAL_ERROR);
+        BN_free(d);
+        BN_free(order);
+        return 0;
+    }
+    BN_free(d);
+    BN_free(order);
+    return gost2001_compute_public(ec);
+}
diff --git a/gost2001_keyx.c b/gost2001_keyx.c
new file mode 100644 (file)
index 0000000..db1bdc1
--- /dev/null
@@ -0,0 +1,292 @@
+/**********************************************************************
+ *                          gost_keyx.c                               *
+ *             Copyright (c) 2005-2006 Cryptocom LTD                  *
+ *         This file is distributed under the same license as OpenSSL *
+ *                                                                    *
+ *   VK0 34.10-2001 key exchange and GOST R 34.10-2001                *
+ *   based PKCS7/SMIME support                                        *
+ *          Requires OpenSSL 0.9.9 for compilation                    *
+ **********************************************************************/
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <string.h>
+#include <openssl/objects.h>
+#include "gost89.h"
+#include "gosthash.h"
+#include "e_gost_err.h"
+#include "gost_keywrap.h"
+#include "gost_lcl.h"
+#include "gost2001_keyx.h"
+
+/* Implementation of CryptoPro VKO 34.10-2001 algorithm */
+static int VKO_compute_key(unsigned char *shared_key, size_t shared_key_size,
+                           const EC_POINT *pub_key, EC_KEY *priv_key,
+                           const unsigned char *ukm)
+{
+    unsigned char ukm_be[8], databuf[64], hashbuf[64];
+    BIGNUM *UKM = NULL, *p = NULL, *order = NULL, *X = NULL, *Y = NULL;
+    const BIGNUM *key = EC_KEY_get0_private_key(priv_key);
+    EC_POINT *pnt = EC_POINT_new(EC_KEY_get0_group(priv_key));
+    int i;
+    gost_hash_ctx hash_ctx;
+    BN_CTX *ctx = BN_CTX_new();
+
+    for (i = 0; i < 8; i++) {
+        ukm_be[7 - i] = ukm[i];
+    }
+    BN_CTX_start(ctx);
+    UKM = getbnfrombuf(ukm_be, 8);
+    p = BN_CTX_get(ctx);
+    order = BN_CTX_get(ctx);
+    X = BN_CTX_get(ctx);
+    Y = BN_CTX_get(ctx);
+    EC_GROUP_get_order(EC_KEY_get0_group(priv_key), order, ctx);
+    BN_mod_mul(p, key, UKM, order, ctx);
+    EC_POINT_mul(EC_KEY_get0_group(priv_key), pnt, NULL, pub_key, p, ctx);
+    EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(priv_key),
+                                        pnt, X, Y, ctx);
+    /*
+     * Serialize elliptic curve point same way as we do it when saving key
+     */
+    store_bignum(Y, databuf, 32);
+    store_bignum(X, databuf + 32, 32);
+    /* And reverse byte order of whole buffer */
+    for (i = 0; i < 64; i++) {
+        hashbuf[63 - i] = databuf[i];
+    }
+    init_gost_hash_ctx(&hash_ctx, &GostR3411_94_CryptoProParamSet);
+    start_hash(&hash_ctx);
+    hash_block(&hash_ctx, hashbuf, 64);
+    finish_hash(&hash_ctx, shared_key);
+    done_gost_hash_ctx(&hash_ctx);
+    BN_free(UKM);
+    BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+    EC_POINT_free(pnt);
+    return 32;
+}
+
+/*
+ * EVP_PKEY_METHOD callback derive. Implements VKO R 34.10-2001
+ * algorithm
+ */
+int pkey_gost2001_derive(EVP_PKEY_CTX *ctx, unsigned char *key,
+                         size_t *keylen)
+{
+    /*
+     * Public key of peer in the ctx field peerkey Our private key in the ctx
+     * pkey ukm is in the algorithm specific context data
+     */
+    EVP_PKEY *my_key = EVP_PKEY_CTX_get0_pkey(ctx);
+    EVP_PKEY *peer_key = EVP_PKEY_CTX_get0_peerkey(ctx);
+    struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(ctx);
+
+    if (!data->shared_ukm) {
+        GOSTerr(GOST_F_PKEY_GOST2001_DERIVE, GOST_R_UKM_NOT_SET);
+        return 0;
+    }
+
+    if (key == NULL) {
+        *keylen = 32;
+        return 32;
+    }
+
+    *keylen =
+        VKO_compute_key(key, 32,
+                        EC_KEY_get0_public_key(EVP_PKEY_get0(peer_key)),
+                        (EC_KEY *)EVP_PKEY_get0(my_key), data->shared_ukm);
+    return 1;
+}
+
+/*
+ * EVP_PKEY_METHOD callback encrypt
+ * Implementation of GOST2001 key transport, cryptocom variation
+ */
+/*
+ * Generates ephemeral key based on pubk algorithm computes shared key using
+ * VKO and returns filled up GOST_KEY_TRANSPORT structure
+ */
+
+/*
+ * EVP_PKEY_METHOD callback encrypt
+ * Implementation of GOST2001 key transport, cryptopo variation
+ */
+
+int pkey_GOST01cp_encrypt(EVP_PKEY_CTX *pctx, unsigned char *out,
+                          size_t *out_len, const unsigned char *key,
+                          size_t key_len)
+{
+    GOST_KEY_TRANSPORT *gkt = NULL;
+    EVP_PKEY *pubk = EVP_PKEY_CTX_get0_pkey(pctx);
+    struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(pctx);
+    const struct gost_cipher_info *param = get_encryption_params(NULL);
+    unsigned char ukm[8], shared_key[32], crypted_key[44];
+    int ret = 0;
+    int key_is_ephemeral = 1;
+    gost_ctx cctx;
+    EVP_PKEY *sec_key = EVP_PKEY_CTX_get0_peerkey(pctx);
+    if (data->shared_ukm) {
+        memcpy(ukm, data->shared_ukm, 8);
+    } else if (out) {
+
+        if (RAND_bytes(ukm, 8) <= 0) {
+            GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
+                    GOST_R_RANDOM_GENERATOR_FAILURE);
+            return 0;
+        }
+    }
+    /* Check for private key in the peer_key of context */
+    if (sec_key) {
+        key_is_ephemeral = 0;
+        if (!gost_get0_priv_key(sec_key)) {
+            GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
+                    GOST_R_NO_PRIVATE_PART_OF_NON_EPHEMERAL_KEYPAIR);
+            goto err;
+        }
+    } else {
+        key_is_ephemeral = 1;
+        if (out) {
+            sec_key = EVP_PKEY_new();
+            EVP_PKEY_assign(sec_key, EVP_PKEY_base_id(pubk), EC_KEY_new());
+            EVP_PKEY_copy_parameters(sec_key, pubk);
+            if (!gost2001_keygen(EVP_PKEY_get0(sec_key))) {
+                goto err;
+            }
+        }
+    }
+    if (!get_gost_engine_param(GOST_PARAM_CRYPT_PARAMS)
+        && param == gost_cipher_list) {
+        param = gost_cipher_list + 1;
+    }
+    if (out) {
+        VKO_compute_key(shared_key, 32,
+                        EC_KEY_get0_public_key(EVP_PKEY_get0(pubk)),
+                        EVP_PKEY_get0(sec_key), ukm);
+        gost_init(&cctx, param->sblock);
+        keyWrapCryptoPro(&cctx, shared_key, ukm, key, crypted_key);
+    }
+    gkt = GOST_KEY_TRANSPORT_new();
+    if (!gkt) {
+        goto err;
+    }
+    if (!ASN1_OCTET_STRING_set(gkt->key_agreement_info->eph_iv, ukm, 8)) {
+        goto err;
+    }
+    if (!ASN1_OCTET_STRING_set(gkt->key_info->imit, crypted_key + 40, 4)) {
+        goto err;
+    }
+    if (!ASN1_OCTET_STRING_set
+        (gkt->key_info->encrypted_key, crypted_key + 8, 32)) {
+        goto err;
+    }
+    if (key_is_ephemeral) {
+        if (!X509_PUBKEY_set
+            (&gkt->key_agreement_info->ephem_key, out ? sec_key : pubk)) {
+            GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
+                    GOST_R_CANNOT_PACK_EPHEMERAL_KEY);
+            goto err;
+        }
+    }
+    ASN1_OBJECT_free(gkt->key_agreement_info->cipher);
+    gkt->key_agreement_info->cipher = OBJ_nid2obj(param->nid);
+    if (key_is_ephemeral && sec_key)
+        EVP_PKEY_free(sec_key);
+    if (!key_is_ephemeral) {
+        /* Set control "public key from client certificate used" */
+        if (EVP_PKEY_CTX_ctrl(pctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 3, NULL)
+            <= 0) {
+            GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT, GOST_R_CTRL_CALL_FAILED);
+            goto err;
+        }
+    }
+    if ((*out_len = i2d_GOST_KEY_TRANSPORT(gkt, out ? &out : NULL)) > 0)
+        ret = 1;
+    GOST_KEY_TRANSPORT_free(gkt);
+    return ret;
+ err:
+    if (key_is_ephemeral && sec_key)
+        EVP_PKEY_free(sec_key);
+    GOST_KEY_TRANSPORT_free(gkt);
+    return -1;
+}
+
+/*
+ * EVP_PKEY_METHOD callback decrypt
+ * Implementation of GOST2001 key transport, cryptopo variation
+ */
+int pkey_GOST01cp_decrypt(EVP_PKEY_CTX *pctx, unsigned char *key,
+                          size_t *key_len, const unsigned char *in,
+                          size_t in_len)
+{
+    const unsigned char *p = in;
+    EVP_PKEY *priv = EVP_PKEY_CTX_get0_pkey(pctx);
+    GOST_KEY_TRANSPORT *gkt = NULL;
+    int ret = 0;
+    unsigned char wrappedKey[44];
+    unsigned char sharedKey[32];
+    gost_ctx ctx;
+    const struct gost_cipher_info *param = NULL;
+    EVP_PKEY *eph_key = NULL, *peerkey = NULL;
+
+    if (!key) {
+        *key_len = 32;
+        return 1;
+    }
+    gkt = d2i_GOST_KEY_TRANSPORT(NULL, (const unsigned char **)&p, in_len);
+    if (!gkt) {
+        GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,
+                GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO);
+        return -1;
+    }
+
+    /* If key transport structure contains public key, use it */
+    eph_key = X509_PUBKEY_get(gkt->key_agreement_info->ephem_key);
+    if (eph_key) {
+        if (EVP_PKEY_derive_set_peer(pctx, eph_key) <= 0) {
+            GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,
+                    GOST_R_INCOMPATIBLE_PEER_KEY);
+            goto err;
+        }
+    } else {
+        /* Set control "public key from client certificate used" */
+        if (EVP_PKEY_CTX_ctrl(pctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 3, NULL)
+            <= 0) {
+            GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT, GOST_R_CTRL_CALL_FAILED);
+            goto err;
+        }
+    }
+    peerkey = EVP_PKEY_CTX_get0_peerkey(pctx);
+    if (!peerkey) {
+        GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT, GOST_R_NO_PEER_KEY);
+        goto err;
+    }
+
+    param = get_encryption_params(gkt->key_agreement_info->cipher);
+    if (!param) {
+        goto err;
+    }
+
+    gost_init(&ctx, param->sblock);
+    OPENSSL_assert(gkt->key_agreement_info->eph_iv->length == 8);
+    memcpy(wrappedKey, gkt->key_agreement_info->eph_iv->data, 8);
+    OPENSSL_assert(gkt->key_info->encrypted_key->length == 32);
+    memcpy(wrappedKey + 8, gkt->key_info->encrypted_key->data, 32);
+    OPENSSL_assert(gkt->key_info->imit->length == 4);
+    memcpy(wrappedKey + 40, gkt->key_info->imit->data, 4);
+    VKO_compute_key(sharedKey, 32,
+                    EC_KEY_get0_public_key(EVP_PKEY_get0(peerkey)),
+                    EVP_PKEY_get0(priv), wrappedKey);
+    if (!keyUnwrapCryptoPro(&ctx, sharedKey, wrappedKey, key)) {
+        GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,
+                GOST_R_ERROR_COMPUTING_SHARED_KEY);
+        goto err;
+    }
+
+    ret = 1;
+ err:
+    if (eph_key)
+        EVP_PKEY_free(eph_key);
+    if (gkt)
+        GOST_KEY_TRANSPORT_free(gkt);
+    return ret;
+}
diff --git a/gost2001_keyx.h b/gost2001_keyx.h
new file mode 100644 (file)
index 0000000..2d29113
--- /dev/null
@@ -0,0 +1,10 @@
+GOST_KEY_TRANSPORT *make_rfc4490_keytransport_2001(EVP_PKEY *pubk,
+                                                   BIGNUM *eph_key,
+                                                   const unsigned char *key,
+                                                   size_t keylen,
+                                                   unsigned char *ukm,
+                                                   size_t ukm_len);
+
+int decrypt_rfc4490_shared_key_2001(EVP_PKEY *priv,
+                                    GOST_KEY_TRANSPORT * gkt,
+                                    unsigned char *key_buf, int key_buf_len);
diff --git a/gost89.c b/gost89.c
new file mode 100644 (file)
index 0000000..4ff4ddd
--- /dev/null
+++ b/gost89.c
@@ -0,0 +1,576 @@
+/**********************************************************************
+ *                        gost89.c                                    *
+ *             Copyright (c) 2005-2006 Cryptocom LTD                  *
+ *         This file is distributed under the same license as OpenSSL *
+ *                                                                    *
+ *          Implementation of GOST 28147-89 encryption algorithm      *
+ *            No OpenSSL libraries required to compile and use        *
+ *                              this code                             *
+ **********************************************************************/
+#include <string.h>
+#include "gost89.h"
+/*-
+   Substitution blocks from RFC 4357
+
+   Note: our implementation of gost 28147-89 algorithm
+   uses S-box matrix rotated 90 degrees counterclockwise, relative to
+   examples given in RFC.
+
+
+*/
+
+/* Substitution blocks from test examples for GOST R 34.11-94*/
+gost_subst_block GostR3411_94_TestParamSet = {
+    {0X1, 0XF, 0XD, 0X0, 0X5, 0X7, 0XA, 0X4, 0X9, 0X2, 0X3, 0XE, 0X6, 0XB,
+     0X8, 0XC}
+    ,
+    {0XD, 0XB, 0X4, 0X1, 0X3, 0XF, 0X5, 0X9, 0X0, 0XA, 0XE, 0X7, 0X6, 0X8,
+     0X2, 0XC}
+    ,
+    {0X4, 0XB, 0XA, 0X0, 0X7, 0X2, 0X1, 0XD, 0X3, 0X6, 0X8, 0X5, 0X9, 0XC,
+     0XF, 0XE}
+    ,
+    {0X6, 0XC, 0X7, 0X1, 0X5, 0XF, 0XD, 0X8, 0X4, 0XA, 0X9, 0XE, 0X0, 0X3,
+     0XB, 0X2}
+    ,
+    {0X7, 0XD, 0XA, 0X1, 0X0, 0X8, 0X9, 0XF, 0XE, 0X4, 0X6, 0XC, 0XB, 0X2,
+     0X5, 0X3}
+    ,
+    {0X5, 0X8, 0X1, 0XD, 0XA, 0X3, 0X4, 0X2, 0XE, 0XF, 0XC, 0X7, 0X6, 0X0,
+     0X9, 0XB}
+    ,
+    {0XE, 0XB, 0X4, 0XC, 0X6, 0XD, 0XF, 0XA, 0X2, 0X3, 0X8, 0X1, 0X0, 0X7,
+     0X5, 0X9}
+    ,
+    {0X4, 0XA, 0X9, 0X2, 0XD, 0X8, 0X0, 0XE, 0X6, 0XB, 0X1, 0XC, 0X7, 0XF,
+     0X5, 0X3}
+};
+
+/* Substitution blocks for hash function 1.2.643.2.9.1.6.1  */
+gost_subst_block GostR3411_94_CryptoProParamSet = {
+    {0x1, 0x3, 0xA, 0x9, 0x5, 0xB, 0x4, 0xF, 0x8, 0x6, 0x7, 0xE, 0xD, 0x0,
+     0x2, 0xC}
+    ,
+    {0xD, 0xE, 0x4, 0x1, 0x7, 0x0, 0x5, 0xA, 0x3, 0xC, 0x8, 0xF, 0x6, 0x2,
+     0x9, 0xB}
+    ,
+    {0x7, 0x6, 0x2, 0x4, 0xD, 0x9, 0xF, 0x0, 0xA, 0x1, 0x5, 0xB, 0x8, 0xE,
+     0xC, 0x3}
+    ,
+    {0x7, 0x6, 0x4, 0xB, 0x9, 0xC, 0x2, 0xA, 0x1, 0x8, 0x0, 0xE, 0xF, 0xD,
+     0x3, 0x5}
+    ,
+    {0x4, 0xA, 0x7, 0xC, 0x0, 0xF, 0x2, 0x8, 0xE, 0x1, 0x6, 0x5, 0xD, 0xB,
+     0x9, 0x3}
+    ,
+    {0x7, 0xF, 0xC, 0xE, 0x9, 0x4, 0x1, 0x0, 0x3, 0xB, 0x5, 0x2, 0x6, 0xA,
+     0x8, 0xD}
+    ,
+    {0x5, 0xF, 0x4, 0x0, 0x2, 0xD, 0xB, 0x9, 0x1, 0x7, 0x6, 0x3, 0xC, 0xE,
+     0xA, 0x8}
+    ,
+    {0xA, 0x4, 0x5, 0x6, 0x8, 0x1, 0x3, 0x7, 0xD, 0xC, 0xE, 0x0, 0x9, 0x2,
+     0xB, 0xF}
+};
+
+/* Test paramset from GOST 28147 */
+gost_subst_block Gost28147_TestParamSet = {
+    {0xC, 0x6, 0x5, 0x2, 0xB, 0x0, 0x9, 0xD, 0x3, 0xE, 0x7, 0xA, 0xF, 0x4,
+     0x1, 0x8}
+    ,
+    {0x9, 0xB, 0xC, 0x0, 0x3, 0x6, 0x7, 0x5, 0x4, 0x8, 0xE, 0xF, 0x1, 0xA,
+     0x2, 0xD}
+    ,
+    {0x8, 0xF, 0x6, 0xB, 0x1, 0x9, 0xC, 0x5, 0xD, 0x3, 0x7, 0xA, 0x0, 0xE,
+     0x2, 0x4}
+    ,
+    {0x3, 0xE, 0x5, 0x9, 0x6, 0x8, 0x0, 0xD, 0xA, 0xB, 0x7, 0xC, 0x2, 0x1,
+     0xF, 0x4}
+    ,
+    {0xE, 0x9, 0xB, 0x2, 0x5, 0xF, 0x7, 0x1, 0x0, 0xD, 0xC, 0x6, 0xA, 0x4,
+     0x3, 0x8}
+    ,
+    {0xD, 0x8, 0xE, 0xC, 0x7, 0x3, 0x9, 0xA, 0x1, 0x5, 0x2, 0x4, 0x6, 0xF,
+     0x0, 0xB}
+    ,
+    {0xC, 0x9, 0xF, 0xE, 0x8, 0x1, 0x3, 0xA, 0x2, 0x7, 0x4, 0xD, 0x6, 0x0,
+     0xB, 0x5}
+    ,
+    {0x4, 0x2, 0xF, 0x5, 0x9, 0x1, 0x0, 0x8, 0xE, 0x3, 0xB, 0xC, 0xD, 0x7,
+     0xA, 0x6}
+};
+
+/* 1.2.643.2.2.31.1 */
+gost_subst_block Gost28147_CryptoProParamSetA = {
+    {0xB, 0xA, 0xF, 0x5, 0x0, 0xC, 0xE, 0x8, 0x6, 0x2, 0x3, 0x9, 0x1, 0x7,
+     0xD, 0x4}
+    ,
+    {0x1, 0xD, 0x2, 0x9, 0x7, 0xA, 0x6, 0x0, 0x8, 0xC, 0x4, 0x5, 0xF, 0x3,
+     0xB, 0xE}
+    ,
+    {0x3, 0xA, 0xD, 0xC, 0x1, 0x2, 0x0, 0xB, 0x7, 0x5, 0x9, 0x4, 0x8, 0xF,
+     0xE, 0x6}
+    ,
+    {0xB, 0x5, 0x1, 0x9, 0x8, 0xD, 0xF, 0x0, 0xE, 0x4, 0x2, 0x3, 0xC, 0x7,
+     0xA, 0x6}
+    ,
+    {0xE, 0x7, 0xA, 0xC, 0xD, 0x1, 0x3, 0x9, 0x0, 0x2, 0xB, 0x4, 0xF, 0x8,
+     0x5, 0x6}
+    ,
+    {0xE, 0x4, 0x6, 0x2, 0xB, 0x3, 0xD, 0x8, 0xC, 0xF, 0x5, 0xA, 0x0, 0x7,
+     0x1, 0x9}
+    ,
+    {0x3, 0x7, 0xE, 0x9, 0x8, 0xA, 0xF, 0x0, 0x5, 0x2, 0x6, 0xC, 0xB, 0x4,
+     0xD, 0x1}
+    ,
+    {0x9, 0x6, 0x3, 0x2, 0x8, 0xB, 0x1, 0x7, 0xA, 0x4, 0xE, 0xF, 0xC, 0x0,
+     0xD, 0x5}
+};
+
+/* 1.2.643.2.2.31.2 */
+gost_subst_block Gost28147_CryptoProParamSetB = {
+    {0x0, 0x4, 0xB, 0xE, 0x8, 0x3, 0x7, 0x1, 0xA, 0x2, 0x9, 0x6, 0xF, 0xD,
+     0x5, 0xC}
+    ,
+    {0x5, 0x2, 0xA, 0xB, 0x9, 0x1, 0xC, 0x3, 0x7, 0x4, 0xD, 0x0, 0x6, 0xF,
+     0x8, 0xE}
+    ,
+    {0x8, 0x3, 0x2, 0x6, 0x4, 0xD, 0xE, 0xB, 0xC, 0x1, 0x7, 0xF, 0xA, 0x0,
+     0x9, 0x5}
+    ,
+    {0x2, 0x7, 0xC, 0xF, 0x9, 0x5, 0xA, 0xB, 0x1, 0x4, 0x0, 0xD, 0x6, 0x8,
+     0xE, 0x3}
+    ,
+    {0x7, 0x5, 0x0, 0xD, 0xB, 0x6, 0x1, 0x2, 0x3, 0xA, 0xC, 0xF, 0x4, 0xE,
+     0x9, 0x8}
+    ,
+    {0xE, 0xC, 0x0, 0xA, 0x9, 0x2, 0xD, 0xB, 0x7, 0x5, 0x8, 0xF, 0x3, 0x6,
+     0x1, 0x4}
+    ,
+    {0x0, 0x1, 0x2, 0xA, 0x4, 0xD, 0x5, 0xC, 0x9, 0x7, 0x3, 0xF, 0xB, 0x8,
+     0x6, 0xE}
+    ,
+    {0x8, 0x4, 0xB, 0x1, 0x3, 0x5, 0x0, 0x9, 0x2, 0xE, 0xA, 0xC, 0xD, 0x6,
+     0x7, 0xF}
+};
+
+/* 1.2.643.2.2.31.3 */
+gost_subst_block Gost28147_CryptoProParamSetC = {
+    {0x7, 0x4, 0x0, 0x5, 0xA, 0x2, 0xF, 0xE, 0xC, 0x6, 0x1, 0xB, 0xD, 0x9,
+     0x3, 0x8}
+    ,
+    {0xA, 0x9, 0x6, 0x8, 0xD, 0xE, 0x2, 0x0, 0xF, 0x3, 0x5, 0xB, 0x4, 0x1,
+     0xC, 0x7}
+    ,
+    {0xC, 0x9, 0xB, 0x1, 0x8, 0xE, 0x2, 0x4, 0x7, 0x3, 0x6, 0x5, 0xA, 0x0,
+     0xF, 0xD}
+    ,
+    {0x8, 0xD, 0xB, 0x0, 0x4, 0x5, 0x1, 0x2, 0x9, 0x3, 0xC, 0xE, 0x6, 0xF,
+     0xA, 0x7}
+    ,
+    {0x3, 0x6, 0x0, 0x1, 0x5, 0xD, 0xA, 0x8, 0xB, 0x2, 0x9, 0x7, 0xE, 0xF,
+     0xC, 0x4}
+    ,
+    {0x8, 0x2, 0x5, 0x0, 0x4, 0x9, 0xF, 0xA, 0x3, 0x7, 0xC, 0xD, 0x6, 0xE,
+     0x1, 0xB}
+    ,
+    {0x0, 0x1, 0x7, 0xD, 0xB, 0x4, 0x5, 0x2, 0x8, 0xE, 0xF, 0xC, 0x9, 0xA,
+     0x6, 0x3}
+    ,
+    {0x1, 0xB, 0xC, 0x2, 0x9, 0xD, 0x0, 0xF, 0x4, 0x5, 0x8, 0xE, 0xA, 0x7,
+     0x6, 0x3}
+};
+
+/* 1.2.643.2.2.31.4 */
+gost_subst_block Gost28147_CryptoProParamSetD = {
+    {0x1, 0xA, 0x6, 0x8, 0xF, 0xB, 0x0, 0x4, 0xC, 0x3, 0x5, 0x9, 0x7, 0xD,
+     0x2, 0xE}
+    ,
+    {0x3, 0x0, 0x6, 0xF, 0x1, 0xE, 0x9, 0x2, 0xD, 0x8, 0xC, 0x4, 0xB, 0xA,
+     0x5, 0x7}
+    ,
+    {0x8, 0x0, 0xF, 0x3, 0x2, 0x5, 0xE, 0xB, 0x1, 0xA, 0x4, 0x7, 0xC, 0x9,
+     0xD, 0x6}
+    ,
+    {0x0, 0xC, 0x8, 0x9, 0xD, 0x2, 0xA, 0xB, 0x7, 0x3, 0x6, 0x5, 0x4, 0xE,
+     0xF, 0x1}
+    ,
+    {0x1, 0x5, 0xE, 0xC, 0xA, 0x7, 0x0, 0xD, 0x6, 0x2, 0xB, 0x4, 0x9, 0x3,
+     0xF, 0x8}
+    ,
+    {0x1, 0xC, 0xB, 0x0, 0xF, 0xE, 0x6, 0x5, 0xA, 0xD, 0x4, 0x8, 0x9, 0x3,
+     0x7, 0x2}
+    ,
+    {0xB, 0x6, 0x3, 0x4, 0xC, 0xF, 0xE, 0x2, 0x7, 0xD, 0x8, 0x0, 0x5, 0xA,
+     0x9, 0x1}
+    ,
+    {0xF, 0xC, 0x2, 0xA, 0x6, 0x4, 0x5, 0x0, 0x7, 0x9, 0xE, 0xD, 0x1, 0xB,
+     0x8, 0x3}
+};
+
+const byte CryptoProKeyMeshingKey[] = {
+    0x69, 0x00, 0x72, 0x22, 0x64, 0xC9, 0x04, 0x23,
+    0x8D, 0x3A, 0xDB, 0x96, 0x46, 0xE9, 0x2A, 0xC4,
+    0x18, 0xFE, 0xAC, 0x94, 0x00, 0xED, 0x07, 0x12,
+    0xC0, 0x86, 0xDC, 0xC2, 0xEF, 0x4C, 0xA9, 0x2B
+};
+
+/* Initialization of gost_ctx subst blocks*/
+static void kboxinit(gost_ctx * c, const gost_subst_block * b)
+{
+    int i;
+
+    for (i = 0; i < 256; i++) {
+        c->k87[i] = (word32) (b->k8[i >> 4] << 4 | b->k7[i & 15]) << 24;
+        c->k65[i] = (b->k6[i >> 4] << 4 | b->k5[i & 15]) << 16;
+        c->k43[i] = (b->k4[i >> 4] << 4 | b->k3[i & 15]) << 8;
+        c->k21[i] = b->k2[i >> 4] << 4 | b->k1[i & 15];
+
+    }
+}
+
+/* Part of GOST 28147 algorithm moved into separate function */
+static word32 f(gost_ctx * c, word32 x)
+{
+    x = c->k87[x >> 24 & 255] | c->k65[x >> 16 & 255] |
+        c->k43[x >> 8 & 255] | c->k21[x & 255];
+    /* Rotate left 11 bits */
+    return x << 11 | x >> (32 - 11);
+}
+
+/* Low-level encryption routine - encrypts one 64 bit block*/
+void gostcrypt(gost_ctx * c, const byte * in, byte * out)
+{
+    register word32 n1, n2;     /* As named in the GOST */
+    n1 = in[0] | (in[1] << 8) | (in[2] << 16) | ((word32) in[3] << 24);
+    n2 = in[4] | (in[5] << 8) | (in[6] << 16) | ((word32) in[7] << 24);
+    /* Instead of swapping halves, swap names each round */
+
+    n2 ^= f(c, n1 + c->k[0]);
+    n1 ^= f(c, n2 + c->k[1]);
+    n2 ^= f(c, n1 + c->k[2]);
+    n1 ^= f(c, n2 + c->k[3]);
+    n2 ^= f(c, n1 + c->k[4]);
+    n1 ^= f(c, n2 + c->k[5]);
+    n2 ^= f(c, n1 + c->k[6]);
+    n1 ^= f(c, n2 + c->k[7]);
+
+    n2 ^= f(c, n1 + c->k[0]);
+    n1 ^= f(c, n2 + c->k[1]);
+    n2 ^= f(c, n1 + c->k[2]);
+    n1 ^= f(c, n2 + c->k[3]);
+    n2 ^= f(c, n1 + c->k[4]);
+    n1 ^= f(c, n2 + c->k[5]);
+    n2 ^= f(c, n1 + c->k[6]);
+    n1 ^= f(c, n2 + c->k[7]);
+
+    n2 ^= f(c, n1 + c->k[0]);
+    n1 ^= f(c, n2 + c->k[1]);
+    n2 ^= f(c, n1 + c->k[2]);
+    n1 ^= f(c, n2 + c->k[3]);
+    n2 ^= f(c, n1 + c->k[4]);
+    n1 ^= f(c, n2 + c->k[5]);
+    n2 ^= f(c, n1 + c->k[6]);
+    n1 ^= f(c, n2 + c->k[7]);
+
+    n2 ^= f(c, n1 + c->k[7]);
+    n1 ^= f(c, n2 + c->k[6]);
+    n2 ^= f(c, n1 + c->k[5]);
+    n1 ^= f(c, n2 + c->k[4]);
+    n2 ^= f(c, n1 + c->k[3]);
+    n1 ^= f(c, n2 + c->k[2]);
+    n2 ^= f(c, n1 + c->k[1]);
+    n1 ^= f(c, n2 + c->k[0]);
+
+    out[0] = (byte) (n2 & 0xff);
+    out[1] = (byte) ((n2 >> 8) & 0xff);
+    out[2] = (byte) ((n2 >> 16) & 0xff);
+    out[3] = (byte) (n2 >> 24);
+    out[4] = (byte) (n1 & 0xff);
+    out[5] = (byte) ((n1 >> 8) & 0xff);
+    out[6] = (byte) ((n1 >> 16) & 0xff);
+    out[7] = (byte) (n1 >> 24);
+}
+
+/* Low-level decryption routine. Decrypts one 64-bit block */
+void gostdecrypt(gost_ctx * c, const byte * in, byte * out)
+{
+    register word32 n1, n2;     /* As named in the GOST */
+    n1 = in[0] | (in[1] << 8) | (in[2] << 16) | ((word32) in[3] << 24);
+    n2 = in[4] | (in[5] << 8) | (in[6] << 16) | ((word32) in[7] << 24);
+
+    n2 ^= f(c, n1 + c->k[0]);
+    n1 ^= f(c, n2 + c->k[1]);
+    n2 ^= f(c, n1 + c->k[2]);
+    n1 ^= f(c, n2 + c->k[3]);
+    n2 ^= f(c, n1 + c->k[4]);
+    n1 ^= f(c, n2 + c->k[5]);
+    n2 ^= f(c, n1 + c->k[6]);
+    n1 ^= f(c, n2 + c->k[7]);
+
+    n2 ^= f(c, n1 + c->k[7]);
+    n1 ^= f(c, n2 + c->k[6]);
+    n2 ^= f(c, n1 + c->k[5]);
+    n1 ^= f(c, n2 + c->k[4]);
+    n2 ^= f(c, n1 + c->k[3]);
+    n1 ^= f(c, n2 + c->k[2]);
+    n2 ^= f(c, n1 + c->k[1]);
+    n1 ^= f(c, n2 + c->k[0]);
+
+    n2 ^= f(c, n1 + c->k[7]);
+    n1 ^= f(c, n2 + c->k[6]);
+    n2 ^= f(c, n1 + c->k[5]);
+    n1 ^= f(c, n2 + c->k[4]);
+    n2 ^= f(c, n1 + c->k[3]);
+    n1 ^= f(c, n2 + c->k[2]);
+    n2 ^= f(c, n1 + c->k[1]);
+    n1 ^= f(c, n2 + c->k[0]);
+
+    n2 ^= f(c, n1 + c->k[7]);
+    n1 ^= f(c, n2 + c->k[6]);
+    n2 ^= f(c, n1 + c->k[5]);
+    n1 ^= f(c, n2 + c->k[4]);
+    n2 ^= f(c, n1 + c->k[3]);
+    n1 ^= f(c, n2 + c->k[2]);
+    n2 ^= f(c, n1 + c->k[1]);
+    n1 ^= f(c, n2 + c->k[0]);
+
+    out[0] = (byte) (n2 & 0xff);
+    out[1] = (byte) ((n2 >> 8) & 0xff);
+    out[2] = (byte) ((n2 >> 16) & 0xff);
+    out[3] = (byte) (n2 >> 24);
+    out[4] = (byte) (n1 & 0xff);
+    out[5] = (byte) ((n1 >> 8) & 0xff);
+    out[6] = (byte) ((n1 >> 16) & 0xff);
+    out[7] = (byte) (n1 >> 24);
+}
+
+/* Encrypts several blocks in ECB mode */
+void gost_enc(gost_ctx * c, const byte * clear, byte * cipher, int blocks)
+{
+    int i;
+    for (i = 0; i < blocks; i++) {
+        gostcrypt(c, clear, cipher);
+        clear += 8;
+        cipher += 8;
+    }
+}
+
+/* Decrypts several blocks in ECB mode */
+void gost_dec(gost_ctx * c, const byte * cipher, byte * clear, int blocks)
+{
+    int i;
+    for (i = 0; i < blocks; i++) {
+        gostdecrypt(c, cipher, clear);
+        clear += 8;
+        cipher += 8;
+    }
+}
+
+/* Encrypts several full blocks in CFB mode using 8byte IV */
+void gost_enc_cfb(gost_ctx * ctx, const byte * iv, const byte * clear,
+                  byte * cipher, int blocks)
+{
+    byte cur_iv[8];
+    byte gamma[8];
+    int i, j;
+    const byte *in;
+    byte *out;
+    memcpy(cur_iv, iv, 8);
+    for (i = 0, in = clear, out = cipher; i < blocks; i++, in += 8, out += 8) {
+        gostcrypt(ctx, cur_iv, gamma);
+        for (j = 0; j < 8; j++) {
+            cur_iv[j] = out[j] = in[j] ^ gamma[j];
+        }
+    }
+}
+
+/* Decrypts several full blocks in CFB mode using 8byte IV */
+void gost_dec_cfb(gost_ctx * ctx, const byte * iv, const byte * cipher,
+                  byte * clear, int blocks)
+{
+    byte cur_iv[8];
+    byte gamma[8];
+    int i, j;
+    const byte *in;
+    byte *out;
+    memcpy(cur_iv, iv, 8);
+    for (i = 0, in = cipher, out = clear; i < blocks; i++, in += 8, out += 8) {
+        gostcrypt(ctx, cur_iv, gamma);
+        for (j = 0; j < 8; j++) {
+            out[j] = (cur_iv[j] = in[j]) ^ gamma[j];
+        }
+    }
+}
+
+/* Encrypts one block using specified key */
+void gost_enc_with_key(gost_ctx * c, byte * key, byte * inblock,
+                       byte * outblock)
+{
+    gost_key(c, key);
+    gostcrypt(c, inblock, outblock);
+}
+
+/* Set 256 bit  key into context */
+void gost_key(gost_ctx * c, const byte * k)
+{
+    int i, j;
+    for (i = 0, j = 0; i < 8; i++, j += 4) {
+        c->k[i] =
+            k[j] | (k[j + 1] << 8) | (k[j + 2] << 16) | ((word32) k[j + 3] <<
+                                                         24);
+    }
+}
+
+/* Retrieve 256-bit key from context */
+void gost_get_key(gost_ctx * c, byte * k)
+{
+    int i, j;
+    for (i = 0, j = 0; i < 8; i++, j += 4) {
+        k[j] = (byte) (c->k[i] & 0xFF);
+        k[j + 1] = (byte) ((c->k[i] >> 8) & 0xFF);
+        k[j + 2] = (byte) ((c->k[i] >> 16) & 0xFF);
+        k[j + 3] = (byte) ((c->k[i] >> 24) & 0xFF);
+    }
+}
+
+/* Initalize context. Provides default value for subst_block */
+void gost_init(gost_ctx * c, const gost_subst_block * b)
+{
+    if (!b) {
+        b = &GostR3411_94_TestParamSet;
+    }
+    kboxinit(c, b);
+}
+
+/* Cleans up key from context */
+void gost_destroy(gost_ctx * c)
+{
+    int i;
+    for (i = 0; i < 8; i++)
+        c->k[i] = 0;
+}
+
+/*
+ * Compute GOST 28147 mac block Parameters gost_ctx *c - context initalized
+ * with substitution blocks and key buffer - 8-byte mac state buffer block
+ * 8-byte block to process.
+ */
+void mac_block(gost_ctx * c, byte * buffer, const byte * block)
+{
+    register word32 n1, n2;     /* As named in the GOST */
+    int i;
+    for (i = 0; i < 8; i++) {
+        buffer[i] ^= block[i];
+    }
+    n1 = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | ((word32)
+                                                             buffer[3] << 24);
+    n2 = buffer[4] | (buffer[5] << 8) | (buffer[6] << 16) | ((word32)
+                                                             buffer[7] << 24);
+    /* Instead of swapping halves, swap names each round */
+
+    n2 ^= f(c, n1 + c->k[0]);
+    n1 ^= f(c, n2 + c->k[1]);
+    n2 ^= f(c, n1 + c->k[2]);
+    n1 ^= f(c, n2 + c->k[3]);
+    n2 ^= f(c, n1 + c->k[4]);
+    n1 ^= f(c, n2 + c->k[5]);
+    n2 ^= f(c, n1 + c->k[6]);
+    n1 ^= f(c, n2 + c->k[7]);
+
+    n2 ^= f(c, n1 + c->k[0]);
+    n1 ^= f(c, n2 + c->k[1]);
+    n2 ^= f(c, n1 + c->k[2]);
+    n1 ^= f(c, n2 + c->k[3]);
+    n2 ^= f(c, n1 + c->k[4]);
+    n1 ^= f(c, n2 + c->k[5]);
+    n2 ^= f(c, n1 + c->k[6]);
+    n1 ^= f(c, n2 + c->k[7]);
+
+    buffer[0] = (byte) (n1 & 0xff);
+    buffer[1] = (byte) ((n1 >> 8) & 0xff);
+    buffer[2] = (byte) ((n1 >> 16) & 0xff);
+    buffer[3] = (byte) (n1 >> 24);
+    buffer[4] = (byte) (n2 & 0xff);
+    buffer[5] = (byte) ((n2 >> 8) & 0xff);
+    buffer[6] = (byte) ((n2 >> 16) & 0xff);
+    buffer[7] = (byte) (n2 >> 24);
+}
+
+/* Get mac with specified number of bits from MAC state buffer */
+void get_mac(byte * buffer, int nbits, byte * out)
+{
+    int nbytes = nbits >> 3;
+    int rembits = nbits & 7;
+    int mask = rembits ? ((1 < rembits) - 1) : 0;
+    int i;
+    for (i = 0; i < nbytes; i++)
+        out[i] = buffer[i];
+    if (rembits)
+        out[i] = buffer[i] & mask;
+}
+
+/*
+ * Compute mac of specified length (in bits) from data. Context should be
+ * initialized with key and subst blocks
+ */
+int gost_mac(gost_ctx * ctx, int mac_len, const unsigned char *data,
+             unsigned int data_len, unsigned char *mac)
+{
+    byte buffer[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+    byte buf2[8];
+    unsigned int i;
+    for (i = 0; i + 8 <= data_len; i += 8)
+        mac_block(ctx, buffer, data + i);
+    if (i < data_len) {
+        memset(buf2, 0, 8);
+        memcpy(buf2, data + i, data_len - i);
+        mac_block(ctx, buffer, buf2);
+        i += 8;
+    }
+    if (i == 8) {
+        memset(buf2, 0, 8);
+        mac_block(ctx, buffer, buf2);
+    }
+    get_mac(buffer, mac_len, mac);
+    return 1;
+}
+
+/* Compute MAC with non-zero IV. Used in some RFC 4357 algorithms */
+int gost_mac_iv(gost_ctx * ctx, int mac_len, const unsigned char *iv,
+                const unsigned char *data, unsigned int data_len,
+                unsigned char *mac)
+{
+    byte buffer[8];
+    byte buf2[8];
+    unsigned int i;
+    memcpy(buffer, iv, 8);
+    for (i = 0; i + 8 <= data_len; i += 8)
+        mac_block(ctx, buffer, data + i);
+    if (i < data_len) {
+        memset(buf2, 0, 8);
+        memcpy(buf2, data + i, data_len - i);
+        mac_block(ctx, buffer, buf2);
+        i += 8;
+    }
+    if (i == 8) {
+        memset(buf2, 0, 8);
+        mac_block(ctx, buffer, buf2);
+    }
+    get_mac(buffer, mac_len, mac);
+    return 1;
+}
+
+/* Implements key meshing algorithm by modifing ctx and IV in place */
+void cryptopro_key_meshing(gost_ctx * ctx, unsigned char *iv)
+{
+    unsigned char newkey[32], newiv[8];
+    /* Set static keymeshing key */
+    /* "Decrypt" key with keymeshing key */
+    gost_dec(ctx, CryptoProKeyMeshingKey, newkey, 4);
+    /* set new key */
+    gost_key(ctx, newkey);
+    /* Encrypt iv with new key */
+    gostcrypt(ctx, iv, newiv);
+    memcpy(iv, newiv, 8);
+}
diff --git a/gost89.h b/gost89.h
new file mode 100644 (file)
index 0000000..e5b877f
--- /dev/null
+++ b/gost89.h
@@ -0,0 +1,98 @@
+/**********************************************************************
+ *                        gost89.h                                    *
+ *             Copyright (c) 2005-2006 Cryptocom LTD                  *
+ *     This file is distributed under the same license as OpenSSL     *
+ *                                                                    *
+ *          Declarations for GOST 28147-89 encryption algorithm       *
+ *            No OpenSSL libraries required to compile and use        *
+ *                       this code                                    *
+ **********************************************************************/
+#ifndef GOST89_H
+# define GOST89_H
+
+/* Typedef for unsigned 32-bit integer */
+# if __LONG_MAX__ > 2147483647L
+typedef unsigned int u4;
+# else
+typedef unsigned long u4;
+# endif
+/* Typedef for unsigned 8-bit integer */
+typedef unsigned char byte;
+
+/* Internal representation of GOST substitution blocks */
+typedef struct {
+    byte k8[16];
+    byte k7[16];
+    byte k6[16];
+    byte k5[16];
+    byte k4[16];
+    byte k3[16];
+    byte k2[16];
+    byte k1[16];
+} gost_subst_block;
+
+/* Cipher context includes key and preprocessed  substitution block */
+typedef struct {
+    u4 k[8];
+    /* Constant s-boxes -- set up in gost_init(). */
+    u4 k87[256], k65[256], k43[256], k21[256];
+} gost_ctx;
+/*
+ * Note: encrypt and decrypt expect full blocks--padding blocks is caller's
+ * responsibility. All bulk encryption is done in ECB mode by these calls.
+ * Other modes may be added easily enough.
+ */
+/* Encrypt several full blocks in ECB mode */
+void gost_enc(gost_ctx * ctx, const byte * clear, byte * cipher, int blocks);
+/* Decrypt several full blocks in ECB mode */
+void gost_dec(gost_ctx * ctx, const byte * cipher, byte * clear, int blocks);
+/* Encrypts several full blocks in CFB mode using 8byte IV */
+void gost_enc_cfb(gost_ctx * ctx, const byte * iv, const byte * clear,
+                  byte * cipher, int blocks);
+/* Decrypts several full blocks in CFB mode using 8byte IV */
+void gost_dec_cfb(gost_ctx * ctx, const byte * iv, const byte * cipher,
+                  byte * clear, int blocks);
+
+/* Encrypt one  block */
+void gostcrypt(gost_ctx * c, const byte * in, byte * out);
+/* Decrypt one  block */
+void gostdecrypt(gost_ctx * c, const byte * in, byte * out);
+/* Set key into context */
+void gost_key(gost_ctx * ctx, const byte * key);
+/* Get key from context */
+void gost_get_key(gost_ctx * ctx, byte * key);
+/* Set S-blocks into context */
+void gost_init(gost_ctx * ctx, const gost_subst_block * subst_block);
+/* Clean up context */
+void gost_destroy(gost_ctx * ctx);
+/* Intermediate function used for calculate hash */
+void gost_enc_with_key(gost_ctx *, byte * key, byte * inblock,
+                       byte * outblock);
+/* Compute MAC of given length in bits from data */
+int gost_mac(gost_ctx * ctx, int hmac_len, const unsigned char *data,
+             unsigned int data_len, unsigned char *hmac);
+/*
+ * Compute MAC of given length in bits from data, using non-zero 8-byte IV
+ * (non-standard, for use in CryptoPro key transport only
+ */
+int gost_mac_iv(gost_ctx * ctx, int hmac_len, const unsigned char *iv,
+                const unsigned char *data, unsigned int data_len,
+                unsigned char *hmac);
+/* Perform one step of MAC calculation like gostcrypt */
+void mac_block(gost_ctx * c, byte * buffer, const byte * block);
+/* Extracts MAC value from mac state buffer */
+void get_mac(byte * buffer, int nbits, byte * out);
+/* Implements cryptopro key meshing algorithm. Expect IV to be 8-byte size*/
+void cryptopro_key_meshing(gost_ctx * ctx, unsigned char *iv);
+/* Parameter sets specified in RFC 4357 */
+extern gost_subst_block GostR3411_94_TestParamSet;
+extern gost_subst_block GostR3411_94_CryptoProParamSet;
+extern gost_subst_block Gost28147_TestParamSet;
+extern gost_subst_block Gost28147_CryptoProParamSetA;
+extern gost_subst_block Gost28147_CryptoProParamSetB;
+extern gost_subst_block Gost28147_CryptoProParamSetC;
+extern gost_subst_block Gost28147_CryptoProParamSetD;
+extern const byte CryptoProKeyMeshingKey[];
+typedef unsigned int word32;
+
+#endif
diff --git a/gost94_keyx.c b/gost94_keyx.c
new file mode 100644 (file)
index 0000000..ce57f17
--- /dev/null
@@ -0,0 +1,280 @@
+/**********************************************************************
+ *                             gost94_keyx.c                          *
+ *             Copyright (c) 2005-2006 Cryptocom LTD                  *
+ *         This file is distributed under the same license as OpenSSL *
+ *                                                                    *
+ *     Implements generation and parsing of GOST_KEY_TRANSPORT for    *
+ *              GOST R 34.10-94 algorithms                            *
+ *                                                                    *
+ *          Requires OpenSSL 0.9.9 for compilation                    *
+ **********************************************************************/
+#include <string.h>
+#include <openssl/dh.h>
+#include <openssl/rand.h>
+#include <openssl/evp.h>
+#include <openssl/objects.h>
+
+#include "gost89.h"
+#include "gosthash.h"
+#include "e_gost_err.h"
+#include "gost_keywrap.h"
+#include "gost_lcl.h"
+/* Common functions for both 94 and 2001 key exchange schemes */
+/*
+ * Implementation of the Diffi-Hellman key agreement scheme based on GOST-94
+ * keys
+ */
+
+/*
+ * Computes Diffie-Hellman key and stores it into buffer in little-endian
+ * byte order as expected by both versions of GOST 94 algorithm
+ */
+static int compute_pair_key_le(unsigned char *pair_key, BIGNUM *pub_key,
+                               DH *dh)
+{
+    unsigned char be_key[128];
+    int i, key_size;
+    key_size = DH_compute_key(be_key, pub_key, dh);
+    if (!key_size)
+        return 0;
+    memset(pair_key, 0, 128);
+    for (i = 0; i < key_size; i++) {
+        pair_key[i] = be_key[key_size - 1 - i];
+    }
+    return key_size;
+}
+
+/*
+ * Computes 256 bit Key exchange key as specified in RFC 4357
+ */
+static int make_cp_exchange_key(BIGNUM *priv_key, EVP_PKEY *pubk,
+                                unsigned char *shared_key)
+{
+    unsigned char dh_key[128];
+    int ret;
+    gost_hash_ctx hash_ctx;
+    DH *dh = DH_new();
+
+    if (!dh)
+        return 0;
+    memset(dh_key, 0, 128);
+    dh->g = BN_dup(pubk->pkey.dsa->g);
+    dh->p = BN_dup(pubk->pkey.dsa->p);
+    dh->priv_key = BN_dup(priv_key);
+    ret =
+        compute_pair_key_le(dh_key, ((DSA *)(EVP_PKEY_get0(pubk)))->pub_key,
+                            dh);
+    DH_free(dh);
+    if (!ret)
+        return 0;
+    init_gost_hash_ctx(&hash_ctx, &GostR3411_94_CryptoProParamSet);
+    start_hash(&hash_ctx);
+    hash_block(&hash_ctx, dh_key, 128);
+    finish_hash(&hash_ctx, shared_key);
+    done_gost_hash_ctx(&hash_ctx);
+    return 1;
+}
+
+/* EVP_PKEY_METHOD callback derive. Implements VKO R 34.10-94 */
+
+int pkey_gost94_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen)
+{
+    EVP_PKEY *pubk = EVP_PKEY_CTX_get0_peerkey(ctx);
+    EVP_PKEY *mykey = EVP_PKEY_CTX_get0_pkey(ctx);
+    *keylen = 32;
+    if (key == NULL)
+        return 1;
+
+    return make_cp_exchange_key(gost_get0_priv_key(mykey), pubk, key);
+}
+
+/*
+ * EVP_PKEY_METHOD callback encrypt for GOST R 34.10-94 cryptopro
+ * modification
+ */
+
+int pkey_GOST94cp_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out,
+                          size_t *outlen, const unsigned char *key,
+                          size_t key_len)
+{
+    GOST_KEY_TRANSPORT *gkt = NULL;
+    unsigned char shared_key[32], ukm[8], crypted_key[44];
+    const struct gost_cipher_info *param = get_encryption_params(NULL);
+    EVP_PKEY *pubk = EVP_PKEY_CTX_get0_pkey(ctx);
+    struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(ctx);
+    gost_ctx cctx;
+    int key_is_ephemeral = 1;
+    int tmp_outlen;
+    EVP_PKEY *mykey = EVP_PKEY_CTX_get0_peerkey(ctx);
+
+    /* Do not use vizir cipher parameters with cryptopro */
+    if (!get_gost_engine_param(GOST_PARAM_CRYPT_PARAMS)
+        && param == gost_cipher_list) {
+        param = gost_cipher_list + 1;
+    }
+
+    if (mykey) {
+        /* If key already set, it is not ephemeral */
+        key_is_ephemeral = 0;
+        if (!gost_get0_priv_key(mykey)) {
+            GOSTerr(GOST_F_PKEY_GOST94CP_ENCRYPT,
+                    GOST_R_NO_PRIVATE_PART_OF_NON_EPHEMERAL_KEYPAIR);
+            goto err;
+        }
+    } else {
+        /* Otherwise generate ephemeral key */
+        key_is_ephemeral = 1;
+        if (out) {
+            mykey = EVP_PKEY_new();
+            EVP_PKEY_assign(mykey, EVP_PKEY_base_id(pubk), DSA_new());
+            EVP_PKEY_copy_parameters(mykey, pubk);
+            if (!gost_sign_keygen(EVP_PKEY_get0(mykey))) {
+                goto err;
+            }
+        }
+    }
+    if (out)
+        make_cp_exchange_key(gost_get0_priv_key(mykey), pubk, shared_key);
+    if (data->shared_ukm) {
+        memcpy(ukm, data->shared_ukm, 8);
+    } else if (out) {
+        if (RAND_bytes(ukm, 8) <= 0) {
+            GOSTerr(GOST_F_PKEY_GOST94CP_ENCRYPT,
+                    GOST_R_RANDOM_GENERATOR_FAILURE);
+            goto err;
+        }
+    }
+
+    if (out) {
+        gost_init(&cctx, param->sblock);
+        keyWrapCryptoPro(&cctx, shared_key, ukm, key, crypted_key);
+    }
+    gkt = GOST_KEY_TRANSPORT_new();
+    if (!gkt) {
+        goto memerr;
+    }
+    if (!ASN1_OCTET_STRING_set(gkt->key_agreement_info->eph_iv, ukm, 8)) {
+        goto memerr;
+    }
+    if (!ASN1_OCTET_STRING_set(gkt->key_info->imit, crypted_key + 40, 4)) {
+        goto memerr;
+    }
+    if (!ASN1_OCTET_STRING_set
+        (gkt->key_info->encrypted_key, crypted_key + 8, 32)) {
+        goto memerr;
+    }
+    if (key_is_ephemeral) {
+        if (!X509_PUBKEY_set
+            (&gkt->key_agreement_info->ephem_key, out ? mykey : pubk)) {
+            GOSTerr(GOST_F_PKEY_GOST94CP_ENCRYPT,
+                    GOST_R_CANNOT_PACK_EPHEMERAL_KEY);
+            goto err;
+        }
+        if (out)
+            EVP_PKEY_free(mykey);
+    }
+    ASN1_OBJECT_free(gkt->key_agreement_info->cipher);
+    gkt->key_agreement_info->cipher = OBJ_nid2obj(param->nid);
+    tmp_outlen = i2d_GOST_KEY_TRANSPORT(gkt, out ? &out : NULL);
+    if (tmp_outlen <= 0) {
+        GOSTerr(GOST_F_PKEY_GOST94CP_ENCRYPT,
+                GOST_R_ERROR_PACKING_KEY_TRANSPORT_INFO);
+        goto err;
+    }
+    *outlen = tmp_outlen;
+    if (!key_is_ephemeral) {
+        /* Set control "public key from client certificate used" */
+        if (EVP_PKEY_CTX_ctrl(ctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 3, NULL) <=
+            0) {
+            GOSTerr(GOST_F_PKEY_GOST94CP_ENCRYPT, GOST_R_CTRL_CALL_FAILED);
+            goto err;
+        }
+    }
+    GOST_KEY_TRANSPORT_free(gkt);
+    return 1;
+ memerr:
+    if (key_is_ephemeral) {
+        EVP_PKEY_free(mykey);
+    }
+    GOSTerr(GOST_F_PKEY_GOST94CP_ENCRYPT, GOST_R_MALLOC_FAILURE);
+ err:
+    GOST_KEY_TRANSPORT_free(gkt);
+    return -1;
+}
+
+/*
+ * EVP_PLEY_METHOD callback decrypt for GOST R 34.10-94 cryptopro
+ * modification
+ */
+int pkey_GOST94cp_decrypt(EVP_PKEY_CTX *ctx, unsigned char *key,
+                          size_t *key_len, const unsigned char *in,
+                          size_t in_len)
+{
+    const unsigned char *p = in;
+    GOST_KEY_TRANSPORT *gkt = NULL;
+    unsigned char wrappedKey[44];
+    unsigned char sharedKey[32];
+    gost_ctx cctx;
+    const struct gost_cipher_info *param = NULL;
+    EVP_PKEY *eph_key = NULL, *peerkey = NULL;
+    EVP_PKEY *priv = EVP_PKEY_CTX_get0_pkey(ctx);
+
+    if (!key) {
+        *key_len = 32;
+        return 1;
+    }
+
+    gkt = d2i_GOST_KEY_TRANSPORT(NULL, (const unsigned char **)&p, in_len);
+    if (!gkt) {
+        GOSTerr(GOST_F_PKEY_GOST94CP_DECRYPT,
+                GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO);
+        return 0;
+    }
+    eph_key = X509_PUBKEY_get(gkt->key_agreement_info->ephem_key);
+    if (eph_key) {
+        if (EVP_PKEY_derive_set_peer(ctx, eph_key) <= 0) {
+            GOSTerr(GOST_F_PKEY_GOST94CP_DECRYPT,
+                    GOST_R_INCOMPATIBLE_PEER_KEY);
+            goto err;
+        }
+    } else {
+        /* Set control "public key from client certificate used" */
+        if (EVP_PKEY_CTX_ctrl(ctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 3, NULL) <=
+            0) {
+            GOSTerr(GOST_F_PKEY_GOST94CP_DECRYPT, GOST_R_CTRL_CALL_FAILED);
+            goto err;
+        }
+    }
+    peerkey = EVP_PKEY_CTX_get0_peerkey(ctx);
+    if (!peerkey) {
+        GOSTerr(GOST_F_PKEY_GOST94CP_DECRYPT, GOST_R_NO_PEER_KEY);
+        goto err;
+    }
+
+    param = get_encryption_params(gkt->key_agreement_info->cipher);
+    if (!param) {
+        goto err;
+    }
+
+    gost_init(&cctx, param->sblock);
+    OPENSSL_assert(gkt->key_agreement_info->eph_iv->length == 8);
+    memcpy(wrappedKey, gkt->key_agreement_info->eph_iv->data, 8);
+    OPENSSL_assert(gkt->key_info->encrypted_key->length == 32);
+    memcpy(wrappedKey + 8, gkt->key_info->encrypted_key->data, 32);
+    OPENSSL_assert(gkt->key_info->imit->length == 4);
+    memcpy(wrappedKey + 40, gkt->key_info->imit->data, 4);
+    make_cp_exchange_key(gost_get0_priv_key(priv), peerkey, sharedKey);
+    if (!keyUnwrapCryptoPro(&cctx, sharedKey, wrappedKey, key)) {
+        GOSTerr(GOST_F_PKEY_GOST94CP_DECRYPT,
+                GOST_R_ERROR_COMPUTING_SHARED_KEY);
+        goto err;
+    }
+
+    EVP_PKEY_free(eph_key);
+    GOST_KEY_TRANSPORT_free(gkt);
+    return 1;
+ err:
+    EVP_PKEY_free(eph_key);
+    GOST_KEY_TRANSPORT_free(gkt);
+    return -1;
+}
diff --git a/gost_ameth.c b/gost_ameth.c
new file mode 100644 (file)
index 0000000..b7c5354
--- /dev/null
@@ -0,0 +1,943 @@
+/**********************************************************************
+ *                          gost_ameth.c                              *
+ *             Copyright (c) 2005-2006 Cryptocom LTD                  *
+ *         This file is distributed under the same license as OpenSSL *
+ *                                                                    *
+ *       Implementation of RFC 4490/4491 ASN1 method                  *
+ *       for OpenSSL                                                  *
+ *          Requires OpenSSL 0.9.9 for compilation                    *
+ **********************************************************************/
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/engine.h>
+#include <openssl/evp.h>
+#include <openssl/asn1.h>
+#ifndef OPENSSL_NO_CMS
+# include <openssl/cms.h>
+#endif
+#include "gost_params.h"
+#include "gost_lcl.h"
+#include "e_gost_err.h"
+
+int gost94_nid_by_params(DSA *p)
+{
+    R3410_params *gost_params;
+    BIGNUM *q = BN_new();
+    for (gost_params = R3410_paramset; gost_params->q != NULL; gost_params++) {
+        BN_dec2bn(&q, gost_params->q);
+        if (!BN_cmp(q, p->q)) {
+            BN_free(q);
+            return gost_params->nid;
+        }
+    }
+    BN_free(q);
+    return NID_undef;
+}
+
+static ASN1_STRING *encode_gost_algor_params(const EVP_PKEY *key)
+{
+    ASN1_STRING *params = ASN1_STRING_new();
+    GOST_KEY_PARAMS *gkp = GOST_KEY_PARAMS_new();
+    int pkey_param_nid = NID_undef;
+
+    if (!params || !gkp) {
+        GOSTerr(GOST_F_ENCODE_GOST_ALGOR_PARAMS, ERR_R_MALLOC_FAILURE);
+        ASN1_STRING_free(params);
+        params = NULL;
+        goto err;
+    }
+    switch (EVP_PKEY_base_id(key)) {
+    case NID_id_GostR3410_2001:
+        pkey_param_nid =
+            EC_GROUP_get_curve_name(EC_KEY_get0_group
+                                    (EVP_PKEY_get0((EVP_PKEY *)key)));
+        break;
+    case NID_id_GostR3410_94:
+        pkey_param_nid =
+            (int)gost94_nid_by_params(EVP_PKEY_get0((EVP_PKEY *)key));
+        if (pkey_param_nid == NID_undef) {
+            GOSTerr(GOST_F_ENCODE_GOST_ALGOR_PARAMS,
+                    GOST_R_INVALID_GOST94_PARMSET);
+            ASN1_STRING_free(params);
+            params = NULL;
+            goto err;
+        }
+        break;
+    }
+    gkp->key_params = OBJ_nid2obj(pkey_param_nid);
+    gkp->hash_params = OBJ_nid2obj(NID_id_GostR3411_94_CryptoProParamSet);
+    /*
+     * gkp->cipher_params = OBJ_nid2obj(cipher_param_nid);
+     */
+    params->length = i2d_GOST_KEY_PARAMS(gkp, &params->data);
+    if (params->length <= 0) {
+        GOSTerr(GOST_F_ENCODE_GOST_ALGOR_PARAMS, ERR_R_MALLOC_FAILURE);
+        ASN1_STRING_free(params);
+        params = NULL;
+        goto err;
+    }
+    params->type = V_ASN1_SEQUENCE;
+ err:
+    GOST_KEY_PARAMS_free(gkp);
+    return params;
+}
+
+/*
+ * Parses GOST algorithm parameters from X509_ALGOR and modifies pkey setting
+ * NID and parameters
+ */
+static int decode_gost_algor_params(EVP_PKEY *pkey, X509_ALGOR *palg)
+{
+    ASN1_OBJECT *palg_obj = NULL;
+    int ptype = V_ASN1_UNDEF;
+    int pkey_nid = NID_undef, param_nid = NID_undef;
+    void *_pval;
+    ASN1_STRING *pval = NULL;
+    const unsigned char *p;
+    GOST_KEY_PARAMS *gkp = NULL;
+
+    X509_ALGOR_get0(&palg_obj, &ptype, &_pval, palg);
+    pval = _pval;
+    if (ptype != V_ASN1_SEQUENCE) {
+        GOSTerr(GOST_F_DECODE_GOST_ALGOR_PARAMS,
+                GOST_R_BAD_KEY_PARAMETERS_FORMAT);
+        return 0;
+    }
+    p = pval->data;
+    pkey_nid = OBJ_obj2nid(palg_obj);
+
+    gkp = d2i_GOST_KEY_PARAMS(NULL, &p, pval->length);
+    if (!gkp) {
+        GOSTerr(GOST_F_DECODE_GOST_ALGOR_PARAMS,
+                GOST_R_BAD_PKEY_PARAMETERS_FORMAT);
+        return 0;
+    }
+    param_nid = OBJ_obj2nid(gkp->key_params);
+    GOST_KEY_PARAMS_free(gkp);
+    if(!EVP_PKEY_set_type(pkey, pkey_nid)) {
+        GOSTerr(GOST_F_DECODE_GOST_ALGOR_PARAMS, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    switch (pkey_nid) {
+    case NID_id_GostR3410_94:
+        {
+            DSA *dsa = EVP_PKEY_get0(pkey);
+            if (!dsa) {
+                dsa = DSA_new();
+                if (!EVP_PKEY_assign(pkey, pkey_nid, dsa))
+                    return 0;
+            }
+            if (!fill_GOST94_params(dsa, param_nid))
+                return 0;
+            break;
+        }
+    case NID_id_GostR3410_2001:
+        {
+            EC_KEY *ec = EVP_PKEY_get0(pkey);
+            if (!ec) {
+                ec = EC_KEY_new();
+                if (!EVP_PKEY_assign(pkey, pkey_nid, ec))
+                    return 0;
+            }
+            if (!fill_GOST2001_params(ec, param_nid))
+                return 0;
+        }
+    }
+
+    return 1;
+}
+
+static int gost_set_priv_key(EVP_PKEY *pkey, BIGNUM *priv)
+{
+    switch (EVP_PKEY_base_id(pkey)) {
+    case NID_id_GostR3410_94:
+        {
+            DSA *dsa = EVP_PKEY_get0(pkey);
+            if (!dsa) {
+                dsa = DSA_new();
+                EVP_PKEY_assign(pkey, EVP_PKEY_base_id(pkey), dsa);
+            }
+            dsa->priv_key = BN_dup(priv);
+            if (!EVP_PKEY_missing_parameters(pkey))
+                gost94_compute_public(dsa);
+            break;
+        }
+    case NID_id_GostR3410_2001:
+        {
+            EC_KEY *ec = EVP_PKEY_get0(pkey);
+            if (!ec) {
+                ec = EC_KEY_new();
+                EVP_PKEY_assign(pkey, EVP_PKEY_base_id(pkey), ec);
+            }
+            if (!EC_KEY_set_private_key(ec, priv))
+                return 0;
+            if (!EVP_PKEY_missing_parameters(pkey))
+                gost2001_compute_public(ec);
+            break;
+        }
+    }
+    return 1;
+}
+
+BIGNUM *gost_get0_priv_key(const EVP_PKEY *pkey)
+{
+    switch (EVP_PKEY_base_id(pkey)) {
+    case NID_id_GostR3410_94:
+        {
+            DSA *dsa = EVP_PKEY_get0((EVP_PKEY *)pkey);
+            if (!dsa) {
+                return NULL;
+            }
+            if (!dsa->priv_key)
+                return NULL;
+            return dsa->priv_key;
+            break;
+        }
+    case NID_id_GostR3410_2001:
+        {
+            EC_KEY *ec = EVP_PKEY_get0((EVP_PKEY *)pkey);
+            const BIGNUM *priv;
+            if (!ec) {
+                return NULL;
+            }
+            if (!(priv = EC_KEY_get0_private_key(ec)))
+                return NULL;
+            return (BIGNUM *)priv;
+            break;
+        }
+    }
+    return NULL;
+}
+
+static int pkey_ctrl_gost(EVP_PKEY *pkey, int op, long arg1, void *arg2)
+{
+    switch (op) {
+    case ASN1_PKEY_CTRL_PKCS7_SIGN:
+        if (arg1 == 0) {
+            X509_ALGOR *alg1 = NULL, *alg2 = NULL;
+            int nid = EVP_PKEY_base_id(pkey);
+            PKCS7_SIGNER_INFO_get0_algs((PKCS7_SIGNER_INFO *)arg2,
+                                        NULL, &alg1, &alg2);
+            X509_ALGOR_set0(alg1, OBJ_nid2obj(NID_id_GostR3411_94),
+                            V_ASN1_NULL, 0);
+            if (nid == NID_undef) {
+                return (-1);
+            }
+            X509_ALGOR_set0(alg2, OBJ_nid2obj(nid), V_ASN1_NULL, 0);
+        }
+        return 1;
+#ifndef OPENSSL_NO_CMS
+    case ASN1_PKEY_CTRL_CMS_SIGN:
+        if (arg1 == 0) {
+            X509_ALGOR *alg1 = NULL, *alg2 = NULL;
+            int nid = EVP_PKEY_base_id(pkey);
+            CMS_SignerInfo_get0_algs((CMS_SignerInfo *)arg2,
+                                     NULL, NULL, &alg1, &alg2);
+            X509_ALGOR_set0(alg1, OBJ_nid2obj(NID_id_GostR3411_94),
+                            V_ASN1_NULL, 0);
+            if (nid == NID_undef) {
+                return (-1);
+            }
+            X509_ALGOR_set0(alg2, OBJ_nid2obj(nid), V_ASN1_NULL, 0);
+        }
+        return 1;
+#endif
+    case ASN1_PKEY_CTRL_PKCS7_ENCRYPT:
+        if (arg1 == 0) {
+            X509_ALGOR *alg;
+            ASN1_STRING *params = encode_gost_algor_params(pkey);
+            if (!params) {
+                return -1;
+            }
+            PKCS7_RECIP_INFO_get0_alg((PKCS7_RECIP_INFO *)arg2, &alg);
+            X509_ALGOR_set0(alg, OBJ_nid2obj(pkey->type),
+                            V_ASN1_SEQUENCE, params);
+        }
+        return 1;
+#ifndef OPENSSL_NO_CMS
+    case ASN1_PKEY_CTRL_CMS_ENVELOPE:
+        if (arg1 == 0) {
+            X509_ALGOR *alg = NULL;
+            ASN1_STRING *params = encode_gost_algor_params(pkey);
+            if (!params) {
+                return -1;
+            }
+            CMS_RecipientInfo_ktri_get0_algs((CMS_RecipientInfo *)arg2, NULL,
+                                             NULL, &alg);
+            X509_ALGOR_set0(alg, OBJ_nid2obj(pkey->type), V_ASN1_SEQUENCE,
+                            params);
+        }
+        return 1;
+#endif
+    case ASN1_PKEY_CTRL_DEFAULT_MD_NID:
+        *(int *)arg2 = NID_id_GostR3411_94;
+        return 2;
+    }
+
+    return -2;
+}
+
+/* --------------------- free functions * ------------------------------*/
+static void pkey_free_gost94(EVP_PKEY *key)
+{
+    if (key->pkey.dsa) {
+        DSA_free(key->pkey.dsa);
+    }
+}
+
+static void pkey_free_gost01(EVP_PKEY *key)
+{
+    if (key->pkey.ec) {
+        EC_KEY_free(key->pkey.ec);
+    }
+}
+
+/* ------------------ private key functions  -----------------------------*/
+static int priv_decode_gost(EVP_PKEY *pk, PKCS8_PRIV_KEY_INFO *p8inf)
+{
+    const unsigned char *pkey_buf = NULL, *p = NULL;
+    int priv_len = 0;
+    BIGNUM *pk_num = NULL;
+    int ret = 0;
+    X509_ALGOR *palg = NULL;
+    ASN1_OBJECT *palg_obj = NULL;
+    ASN1_INTEGER *priv_key = NULL;
+
+    if (!PKCS8_pkey_get0(&palg_obj, &pkey_buf, &priv_len, &palg, p8inf))
+        return 0;
+    p = pkey_buf;
+    if (!decode_gost_algor_params(pk, palg)) {
+        return 0;
+    }
+    if (V_ASN1_OCTET_STRING == *p) {
+        /* New format - Little endian octet string */
+        unsigned char rev_buf[32];
+        int i;
+        ASN1_OCTET_STRING *s = d2i_ASN1_OCTET_STRING(NULL, &p, priv_len);
+        if (!s || s->length != 32) {
+            GOSTerr(GOST_F_PRIV_DECODE_GOST, EVP_R_DECODE_ERROR);
+            return 0;
+        }
+        for (i = 0; i < 32; i++) {
+            rev_buf[31 - i] = s->data[i];
+        }
+        ASN1_STRING_free(s);
+        pk_num = getbnfrombuf(rev_buf, 32);
+    } else {
+        priv_key = d2i_ASN1_INTEGER(NULL, &p, priv_len);
+        if (!priv_key)
+            return 0;
+        ret = ((pk_num = ASN1_INTEGER_to_BN(priv_key, NULL)) != NULL);
+        ASN1_INTEGER_free(priv_key);
+        if (!ret) {
+            GOSTerr(GOST_F_PRIV_DECODE_GOST, EVP_R_DECODE_ERROR);
+            return 0;
+        }
+    }
+
+    ret = gost_set_priv_key(pk, pk_num);
+    BN_free(pk_num);
+    return ret;
+}
+
+/* ----------------------------------------------------------------------*/
+static int priv_encode_gost(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pk)
+{
+    ASN1_OBJECT *algobj = OBJ_nid2obj(EVP_PKEY_base_id(pk));
+    ASN1_STRING *params = encode_gost_algor_params(pk);
+    unsigned char *priv_buf = NULL;
+    int priv_len;
+
+    ASN1_INTEGER *asn1key = NULL;
+    if (!params) {
+        return 0;
+    }
+    asn1key = BN_to_ASN1_INTEGER(gost_get0_priv_key(pk), NULL);
+    priv_len = i2d_ASN1_INTEGER(asn1key, &priv_buf);
+    ASN1_INTEGER_free(asn1key);
+    return PKCS8_pkey_set0(p8, algobj, 0, V_ASN1_SEQUENCE, params,
+                           priv_buf, priv_len);
+}
+
+/* --------- printing keys --------------------------------*/
+static int print_gost_94(BIO *out, const EVP_PKEY *pkey, int indent,
+                         ASN1_PCTX *pctx, int type)
+{
+    int param_nid = NID_undef;
+
+    if (type == 2) {
+        BIGNUM *key;
+
+        if (!BIO_indent(out, indent, 128))
+            return 0;
+        BIO_printf(out, "Private key: ");
+        key = gost_get0_priv_key(pkey);
+        if (!key)
+            BIO_printf(out, "<undefined>");
+        else
+            BN_print(out, key);
+        BIO_printf(out, "\n");
+    }
+    if (type >= 1) {
+        BIGNUM *pubkey;
+
+        pubkey = ((DSA *)EVP_PKEY_get0((EVP_PKEY *)pkey))->pub_key;
+        BIO_indent(out, indent, 128);
+        BIO_printf(out, "Public key: ");
+        BN_print(out, pubkey);
+        BIO_printf(out, "\n");
+    }
+
+    param_nid = gost94_nid_by_params(EVP_PKEY_get0((EVP_PKEY *)pkey));
+    BIO_indent(out, indent, 128);
+    BIO_printf(out, "Parameter set: %s\n", OBJ_nid2ln(param_nid));
+    return 1;
+}
+
+static int param_print_gost94(BIO *out, const EVP_PKEY *pkey, int indent,
+                              ASN1_PCTX *pctx)
+{
+    return print_gost_94(out, pkey, indent, pctx, 0);
+}
+
+static int pub_print_gost94(BIO *out, const EVP_PKEY *pkey, int indent,
+                            ASN1_PCTX *pctx)
+{
+    return print_gost_94(out, pkey, indent, pctx, 1);
+}
+
+static int priv_print_gost94(BIO *out, const EVP_PKEY *pkey, int indent,
+                             ASN1_PCTX *pctx)
+{
+    return print_gost_94(out, pkey, indent, pctx, 2);
+}
+
+static int print_gost_01(BIO *out, const EVP_PKEY *pkey, int indent,
+                         ASN1_PCTX *pctx, int type)
+{
+    int param_nid = NID_undef;
+    if (type == 2) {
+        BIGNUM *key;
+
+        if (!BIO_indent(out, indent, 128))
+            return 0;
+        BIO_printf(out, "Private key: ");
+        key = gost_get0_priv_key(pkey);
+        if (!key)
+            BIO_printf(out, "<undefined)");
+        else
+            BN_print(out, key);
+        BIO_printf(out, "\n");
+    }
+    if (type >= 1) {
+        BN_CTX *ctx = BN_CTX_new();
+        BIGNUM *X, *Y;
+        const EC_POINT *pubkey;
+        const EC_GROUP *group;
+
+        if (!ctx) {
+            GOSTerr(GOST_F_PRINT_GOST_01, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        BN_CTX_start(ctx);
+        X = BN_CTX_get(ctx);
+        Y = BN_CTX_get(ctx);
+        pubkey =
+            EC_KEY_get0_public_key((EC_KEY *)EVP_PKEY_get0((EVP_PKEY *)pkey));
+        group = EC_KEY_get0_group((EC_KEY *)EVP_PKEY_get0((EVP_PKEY *)pkey));
+        if (!EC_POINT_get_affine_coordinates_GFp(group, pubkey, X, Y, ctx)) {
+            GOSTerr(GOST_F_PRINT_GOST_01, ERR_R_EC_LIB);
+            BN_CTX_free(ctx);
+            return 0;
+        }
+        if (!BIO_indent(out, indent, 128))
+            return 0;
+        BIO_printf(out, "Public key:\n");
+        if (!BIO_indent(out, indent + 3, 128))
+            return 0;
+        BIO_printf(out, "X:");
+        BN_print(out, X);
+        BIO_printf(out, "\n");
+        BIO_indent(out, indent + 3, 128);
+        BIO_printf(out, "Y:");
+        BN_print(out, Y);
+        BIO_printf(out, "\n");
+        BN_CTX_end(ctx);
+        BN_CTX_free(ctx);
+    }
+
+    param_nid =
+        EC_GROUP_get_curve_name(EC_KEY_get0_group
+                                (EVP_PKEY_get0((EVP_PKEY *)pkey)));
+    if (!BIO_indent(out, indent, 128))
+        return 0;
+    BIO_printf(out, "Parameter set: %s\n", OBJ_nid2ln(param_nid));
+    return 1;
+}
+
+static int param_print_gost01(BIO *out, const EVP_PKEY *pkey, int indent,
+                              ASN1_PCTX *pctx)
+{
+    return print_gost_01(out, pkey, indent, pctx, 0);
+}
+
+static int pub_print_gost01(BIO *out, const EVP_PKEY *pkey, int indent,
+                            ASN1_PCTX *pctx)
+{
+    return print_gost_01(out, pkey, indent, pctx, 1);
+}
+
+static int priv_print_gost01(BIO *out, const EVP_PKEY *pkey, int indent,
+                             ASN1_PCTX *pctx)
+{
+    return print_gost_01(out, pkey, indent, pctx, 2);
+}
+
+/* ---------------------------------------------------------------------*/
+static int param_missing_gost94(const EVP_PKEY *pk)
+{
+    const DSA *dsa = EVP_PKEY_get0((EVP_PKEY *)pk);
+    if (!dsa)
+        return 1;
+    if (!dsa->q)
+        return 1;
+    return 0;
+}
+
+static int param_missing_gost01(const EVP_PKEY *pk)
+{
+    const EC_KEY *ec = EVP_PKEY_get0((EVP_PKEY *)pk);
+    if (!ec)
+        return 1;
+    if (!EC_KEY_get0_group(ec))
+        return 1;
+    return 0;
+}
+
+static int param_copy_gost94(EVP_PKEY *to, const EVP_PKEY *from)
+{
+    const DSA *dfrom = EVP_PKEY_get0((EVP_PKEY *)from);
+    DSA *dto = EVP_PKEY_get0(to);
+    if (EVP_PKEY_base_id(from) != EVP_PKEY_base_id(to)) {
+        GOSTerr(GOST_F_PARAM_COPY_GOST94, GOST_R_INCOMPATIBLE_ALGORITHMS);
+        return 0;
+    }
+    if (!dfrom) {
+        GOSTerr(GOST_F_PARAM_COPY_GOST94, GOST_R_KEY_PARAMETERS_MISSING);
+        return 0;
+    }
+    if (!dto) {
+        dto = DSA_new();
+        EVP_PKEY_assign(to, EVP_PKEY_base_id(from), dto);
+    }
+#define COPYBIGNUM(a,b,x) if (a->x) BN_free(a->x); a->x=BN_dup(b->x);
+    COPYBIGNUM(dto, dfrom, p)
+        COPYBIGNUM(dto, dfrom, q)
+        COPYBIGNUM(dto, dfrom, g)
+
+        if (dto->priv_key)
+        gost94_compute_public(dto);
+    return 1;
+}
+
+static int param_copy_gost01(EVP_PKEY *to, const EVP_PKEY *from)
+{
+    EC_KEY *eto = EVP_PKEY_get0(to);
+    const EC_KEY *efrom = EVP_PKEY_get0((EVP_PKEY *)from);
+    if (EVP_PKEY_base_id(from) != EVP_PKEY_base_id(to)) {
+        GOSTerr(GOST_F_PARAM_COPY_GOST01, GOST_R_INCOMPATIBLE_ALGORITHMS);
+        return 0;
+    }
+    if (!efrom) {
+        GOSTerr(GOST_F_PARAM_COPY_GOST01, GOST_R_KEY_PARAMETERS_MISSING);
+        return 0;
+    }
+    if (!eto) {
+        eto = EC_KEY_new();
+        if(!eto) {
+            GOSTerr(GOST_F_PARAM_COPY_GOST01, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        if(!EVP_PKEY_assign(to, EVP_PKEY_base_id(from), eto)) {
+            GOSTerr(GOST_F_PARAM_COPY_GOST01, ERR_R_INTERNAL_ERROR);
+            return 0;
+        }
+    }
+    if(!EC_KEY_set_group(eto, EC_KEY_get0_group(efrom))) {
+        GOSTerr(GOST_F_PARAM_COPY_GOST01, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    if (EC_KEY_get0_private_key(eto)) {
+        gost2001_compute_public(eto);
+    }
+    return 1;
+}
+
+static int param_cmp_gost94(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+    const DSA *da = EVP_PKEY_get0((EVP_PKEY *)a);
+    const DSA *db = EVP_PKEY_get0((EVP_PKEY *)b);
+    if (!BN_cmp(da->q, db->q))
+        return 1;
+    return 0;
+}
+
+static int param_cmp_gost01(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+    if (EC_GROUP_get_curve_name
+        (EC_KEY_get0_group(EVP_PKEY_get0((EVP_PKEY *)a))) ==
+        EC_GROUP_get_curve_name(EC_KEY_get0_group
+                                (EVP_PKEY_get0((EVP_PKEY *)b)))) {
+        return 1;
+    }
+    return 0;
+
+}
+
+/* ---------- Public key functions * --------------------------------------*/
+static int pub_decode_gost94(EVP_PKEY *pk, X509_PUBKEY *pub)
+{
+    X509_ALGOR *palg = NULL;
+    const unsigned char *pubkey_buf = NULL;
+    unsigned char *databuf;
+    ASN1_OBJECT *palgobj = NULL;
+    int pub_len, i, j;
+    DSA *dsa;
+    ASN1_OCTET_STRING *octet = NULL;
+
+    if (!X509_PUBKEY_get0_param(&palgobj, &pubkey_buf, &pub_len, &palg, pub))
+        return 0;
+    EVP_PKEY_assign(pk, OBJ_obj2nid(palgobj), NULL);
+    if (!decode_gost_algor_params(pk, palg))
+        return 0;
+    octet = d2i_ASN1_OCTET_STRING(NULL, &pubkey_buf, pub_len);
+    if (!octet) {
+        GOSTerr(GOST_F_PUB_DECODE_GOST94, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    databuf = OPENSSL_malloc(octet->length);
+    for (i = 0, j = octet->length - 1; i < octet->length; i++, j--) {
+        databuf[j] = octet->data[i];
+    }
+    dsa = EVP_PKEY_get0(pk);
+    dsa->pub_key = BN_bin2bn(databuf, octet->length, NULL);
+    ASN1_OCTET_STRING_free(octet);
+    OPENSSL_free(databuf);
+    return 1;
+
+}
+
+static int pub_encode_gost94(X509_PUBKEY *pub, const EVP_PKEY *pk)
+{
+    ASN1_OBJECT *algobj = NULL;
+    ASN1_OCTET_STRING *octet = NULL;
+    void *pval = NULL;
+    unsigned char *buf = NULL, *databuf, *sptr;
+    int i, j, data_len, ret = 0;
+
+    int ptype = V_ASN1_UNDEF;
+    DSA *dsa = EVP_PKEY_get0((EVP_PKEY *)pk);
+    algobj = OBJ_nid2obj(EVP_PKEY_base_id(pk));
+    if (pk->save_parameters) {
+        ASN1_STRING *params = encode_gost_algor_params(pk);
+        pval = params;
+        ptype = V_ASN1_SEQUENCE;
+    }
+    data_len = BN_num_bytes(dsa->pub_key);
+    databuf = OPENSSL_malloc(data_len);
+    BN_bn2bin(dsa->pub_key, databuf);
+    octet = ASN1_OCTET_STRING_new();
+    ASN1_STRING_set(octet, NULL, data_len);
+    sptr = ASN1_STRING_data(octet);
+    for (i = 0, j = data_len - 1; i < data_len; i++, j--) {
+        sptr[i] = databuf[j];
+    }
+    OPENSSL_free(databuf);
+    ret = i2d_ASN1_OCTET_STRING(octet, &buf);
+    ASN1_BIT_STRING_free(octet);
+    if (ret < 0)
+        return 0;
+    return X509_PUBKEY_set0_param(pub, algobj, ptype, pval, buf, ret);
+}
+
+static int pub_decode_gost01(EVP_PKEY *pk, X509_PUBKEY *pub)
+{
+    X509_ALGOR *palg = NULL;
+    const unsigned char *pubkey_buf = NULL;
+    unsigned char *databuf;
+    ASN1_OBJECT *palgobj = NULL;
+    int pub_len, i, j;
+    EC_POINT *pub_key;
+    BIGNUM *X, *Y;
+    ASN1_OCTET_STRING *octet = NULL;
+    int len;
+    const EC_GROUP *group;
+
+    if (!X509_PUBKEY_get0_param(&palgobj, &pubkey_buf, &pub_len, &palg, pub))
+        return 0;
+    EVP_PKEY_assign(pk, OBJ_obj2nid(palgobj), NULL);
+    if (!decode_gost_algor_params(pk, palg))
+        return 0;
+    group = EC_KEY_get0_group(EVP_PKEY_get0(pk));
+    octet = d2i_ASN1_OCTET_STRING(NULL, &pubkey_buf, pub_len);
+    if (!octet) {
+        GOSTerr(GOST_F_PUB_DECODE_GOST01, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    databuf = OPENSSL_malloc(octet->length);
+    for (i = 0, j = octet->length - 1; i < octet->length; i++, j--) {
+        databuf[j] = octet->data[i];
+    }
+    len = octet->length / 2;
+    ASN1_OCTET_STRING_free(octet);
+
+    Y = getbnfrombuf(databuf, len);
+    X = getbnfrombuf(databuf + len, len);
+    OPENSSL_free(databuf);
+    pub_key = EC_POINT_new(group);
+    if (!EC_POINT_set_affine_coordinates_GFp(group, pub_key, X, Y, NULL)) {
+        GOSTerr(GOST_F_PUB_DECODE_GOST01, ERR_R_EC_LIB);
+        EC_POINT_free(pub_key);
+        BN_free(X);
+        BN_free(Y);
+        return 0;
+    }
+    BN_free(X);
+    BN_free(Y);
+    if (!EC_KEY_set_public_key(EVP_PKEY_get0(pk), pub_key)) {
+        GOSTerr(GOST_F_PUB_DECODE_GOST01, ERR_R_EC_LIB);
+        EC_POINT_free(pub_key);
+        return 0;
+    }
+    EC_POINT_free(pub_key);
+    return 1;
+
+}
+
+static int pub_encode_gost01(X509_PUBKEY *pub, const EVP_PKEY *pk)
+{
+    ASN1_OBJECT *algobj = NULL;
+    ASN1_OCTET_STRING *octet = NULL;
+    void *pval = NULL;
+    unsigned char *buf = NULL, *databuf, *sptr;
+    int i, j, data_len, ret = 0;
+    const EC_POINT *pub_key;
+    BIGNUM *X, *Y, *order;
+    const EC_KEY *ec = EVP_PKEY_get0((EVP_PKEY *)pk);
+    int ptype = V_ASN1_UNDEF;
+
+    algobj = OBJ_nid2obj(EVP_PKEY_base_id(pk));
+    if (pk->save_parameters) {
+        ASN1_STRING *params = encode_gost_algor_params(pk);
+        pval = params;
+        ptype = V_ASN1_SEQUENCE;
+    }
+    order = BN_new();
+    EC_GROUP_get_order(EC_KEY_get0_group(ec), order, NULL);
+    pub_key = EC_KEY_get0_public_key(ec);
+    if (!pub_key) {
+        GOSTerr(GOST_F_PUB_ENCODE_GOST01, GOST_R_PUBLIC_KEY_UNDEFINED);
+        return 0;
+    }
+    X = BN_new();
+    Y = BN_new();
+    if(!X || !Y) {
+        GOSTerr(GOST_F_PUB_ENCODE_GOST01, ERR_R_MALLOC_FAILURE);
+        if(X) BN_free(X);
+        if(Y) BN_free(Y);
+        BN_free(order);
+        return 0;
+    }
+    if(!EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec),
+                                        pub_key, X, Y, NULL)) {
+        GOSTerr(GOST_F_PUB_ENCODE_GOST01, ERR_R_INTERNAL_ERROR);
+        BN_free(X);
+        BN_free(Y);
+        BN_free(order);
+        return 0;
+    }
+    data_len = 2 * BN_num_bytes(order);
+    BN_free(order);
+    databuf = OPENSSL_malloc(data_len);
+    memset(databuf, 0, data_len);
+
+    store_bignum(X, databuf + data_len / 2, data_len / 2);
+    store_bignum(Y, databuf, data_len / 2);
+
+    BN_free(X);
+    BN_free(Y);
+    octet = ASN1_OCTET_STRING_new();
+    ASN1_STRING_set(octet, NULL, data_len);
+    sptr = ASN1_STRING_data(octet);
+    for (i = 0, j = data_len - 1; i < data_len; i++, j--) {
+        sptr[i] = databuf[j];
+    }
+    OPENSSL_free(databuf);
+    ret = i2d_ASN1_OCTET_STRING(octet, &buf);
+    ASN1_BIT_STRING_free(octet);
+    if (ret < 0)
+        return 0;
+    return X509_PUBKEY_set0_param(pub, algobj, ptype, pval, buf, ret);
+}
+
+static int pub_cmp_gost94(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+    const DSA *da = EVP_PKEY_get0((EVP_PKEY *)a);
+    const DSA *db = EVP_PKEY_get0((EVP_PKEY *)b);
+    if (da && db && da->pub_key && db->pub_key
+        && !BN_cmp(da->pub_key, db->pub_key)) {
+        return 1;
+    }
+    return 0;
+}
+
+static int pub_cmp_gost01(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+    const EC_KEY *ea = EVP_PKEY_get0((EVP_PKEY *)a);
+    const EC_KEY *eb = EVP_PKEY_get0((EVP_PKEY *)b);
+    const EC_POINT *ka, *kb;
+    int ret = 0;
+    if (!ea || !eb)
+        return 0;
+    ka = EC_KEY_get0_public_key(ea);
+    kb = EC_KEY_get0_public_key(eb);
+    if (!ka || !kb)
+        return 0;
+    ret = (0 == EC_POINT_cmp(EC_KEY_get0_group(ea), ka, kb, NULL));
+    return ret;
+}
+
+static int pkey_size_gost(const EVP_PKEY *pk)
+{
+    return 64;
+}
+
+static int pkey_bits_gost(const EVP_PKEY *pk)
+{
+    return 256;
+}
+
+/* ---------------------- ASN1 METHOD for GOST MAC  -------------------*/
+static void mackey_free_gost(EVP_PKEY *pk)
+{
+    if (pk->pkey.ptr) {
+        OPENSSL_free(pk->pkey.ptr);
+    }
+}
+
+static int mac_ctrl_gost(EVP_PKEY *pkey, int op, long arg1, void *arg2)
+{
+    switch (op) {
+    case ASN1_PKEY_CTRL_DEFAULT_MD_NID:
+        *(int *)arg2 = NID_id_Gost28147_89_MAC;
+        return 2;
+    }
+    return -2;
+}
+
+static int gost94_param_encode(const EVP_PKEY *pkey, unsigned char **pder)
+{
+    int nid = gost94_nid_by_params(EVP_PKEY_get0((EVP_PKEY *)pkey));
+    return i2d_ASN1_OBJECT(OBJ_nid2obj(nid), pder);
+}
+
+static int gost2001_param_encode(const EVP_PKEY *pkey, unsigned char **pder)
+{
+    int nid =
+        EC_GROUP_get_curve_name(EC_KEY_get0_group
+                                (EVP_PKEY_get0((EVP_PKEY *)pkey)));
+    return i2d_ASN1_OBJECT(OBJ_nid2obj(nid), pder);
+}
+
+static int gost94_param_decode(EVP_PKEY *pkey, const unsigned char **pder,
+                               int derlen)
+{
+    ASN1_OBJECT *obj = NULL;
+    DSA *dsa = EVP_PKEY_get0(pkey);
+    int nid;
+    if (d2i_ASN1_OBJECT(&obj, pder, derlen) == NULL) {
+        return 0;
+    }
+    nid = OBJ_obj2nid(obj);
+    ASN1_OBJECT_free(obj);
+    if (!dsa) {
+        dsa = DSA_new();
+        if (!EVP_PKEY_assign(pkey, NID_id_GostR3410_94, dsa))
+            return 0;
+    }
+    if (!fill_GOST94_params(dsa, nid))
+        return 0;
+    return 1;
+}
+
+static int gost2001_param_decode(EVP_PKEY *pkey, const unsigned char **pder,
+                                 int derlen)
+{
+    ASN1_OBJECT *obj = NULL;
+    int nid;
+    EC_KEY *ec = EVP_PKEY_get0(pkey);
+    if (d2i_ASN1_OBJECT(&obj, pder, derlen) == NULL) {
+        return 0;
+    }
+    nid = OBJ_obj2nid(obj);
+    ASN1_OBJECT_free(obj);
+    if (!ec) {
+        ec = EC_KEY_new();
+        if (!EVP_PKEY_assign(pkey, NID_id_GostR3410_2001, ec))
+            return 0;
+    }
+    if (!fill_GOST2001_params(ec, nid))
+        return 0;
+    return 1;
+}
+
+/* ----------------------------------------------------------------------*/
+int register_ameth_gost(int nid, EVP_PKEY_ASN1_METHOD **ameth,
+                        const char *pemstr, const char *info)
+{
+    *ameth = EVP_PKEY_asn1_new(nid, ASN1_PKEY_SIGPARAM_NULL, pemstr, info);
+    if (!*ameth)
+        return 0;
+    switch (nid) {
+    case NID_id_GostR3410_94:
+        EVP_PKEY_asn1_set_free(*ameth, pkey_free_gost94);
+        EVP_PKEY_asn1_set_private(*ameth,
+                                  priv_decode_gost, priv_encode_gost,
+                                  priv_print_gost94);
+
+        EVP_PKEY_asn1_set_param(*ameth,
+                                gost94_param_decode, gost94_param_encode,
+                                param_missing_gost94, param_copy_gost94,
+                                param_cmp_gost94, param_print_gost94);
+        EVP_PKEY_asn1_set_public(*ameth,
+                                 pub_decode_gost94, pub_encode_gost94,
+                                 pub_cmp_gost94, pub_print_gost94,
+                                 pkey_size_gost, pkey_bits_gost);
+
+        EVP_PKEY_asn1_set_ctrl(*ameth, pkey_ctrl_gost);
+        break;
+    case NID_id_GostR3410_2001:
+        EVP_PKEY_asn1_set_free(*ameth, pkey_free_gost01);
+        EVP_PKEY_asn1_set_private(*ameth,
+                                  priv_decode_gost, priv_encode_gost,
+                                  priv_print_gost01);
+
+        EVP_PKEY_asn1_set_param(*ameth,
+                                gost2001_param_decode, gost2001_param_encode,
+                                param_missing_gost01, param_copy_gost01,
+                                param_cmp_gost01, param_print_gost01);
+        EVP_PKEY_asn1_set_public(*ameth,
+                                 pub_decode_gost01, pub_encode_gost01,
+                                 pub_cmp_gost01, pub_print_gost01,
+                                 pkey_size_gost, pkey_bits_gost);
+
+        EVP_PKEY_asn1_set_ctrl(*ameth, pkey_ctrl_gost);
+        break;
+    case NID_id_Gost28147_89_MAC:
+        EVP_PKEY_asn1_set_free(*ameth, mackey_free_gost);
+        EVP_PKEY_asn1_set_ctrl(*ameth, mac_ctrl_gost);
+        break;
+    }
+    return 1;
+}
diff --git a/gost_asn1.c b/gost_asn1.c
new file mode 100644 (file)
index 0000000..1168633
--- /dev/null
@@ -0,0 +1,56 @@
+/**********************************************************************
+ *                          gost_keytrans.c                           *
+ *             Copyright (c) 2005-2006 Cryptocom LTD                  *
+ *         This file is distributed under the same license as OpenSSL *
+ *                                                                    *
+ *   ASN1 structure definition for GOST key transport                 *
+ *          Requires OpenSSL 0.9.9 for compilation                    *
+ **********************************************************************/
+#include <stdio.h>
+#include <openssl/asn1t.h>
+#include <openssl/x509.h>
+#include "gost_lcl.h"
+
+ASN1_NDEF_SEQUENCE(GOST_KEY_TRANSPORT) = {
+        ASN1_SIMPLE(GOST_KEY_TRANSPORT, key_info, GOST_KEY_INFO),
+        ASN1_IMP(GOST_KEY_TRANSPORT, key_agreement_info, GOST_KEY_AGREEMENT_INFO, 0)
+} ASN1_NDEF_SEQUENCE_END(GOST_KEY_TRANSPORT)
+
+IMPLEMENT_ASN1_FUNCTIONS(GOST_KEY_TRANSPORT)
+
+ASN1_NDEF_SEQUENCE(GOST_KEY_INFO) = {
+        ASN1_SIMPLE(GOST_KEY_INFO, encrypted_key, ASN1_OCTET_STRING),
+        ASN1_SIMPLE(GOST_KEY_INFO, imit,          ASN1_OCTET_STRING)
+} ASN1_NDEF_SEQUENCE_END(GOST_KEY_INFO)
+
+IMPLEMENT_ASN1_FUNCTIONS(GOST_KEY_INFO)
+
+ASN1_NDEF_SEQUENCE(GOST_KEY_AGREEMENT_INFO) = {
+        ASN1_SIMPLE(GOST_KEY_AGREEMENT_INFO, cipher, ASN1_OBJECT),
+        ASN1_IMP_OPT(GOST_KEY_AGREEMENT_INFO, ephem_key, X509_PUBKEY, 0),
+        ASN1_SIMPLE(GOST_KEY_AGREEMENT_INFO, eph_iv, ASN1_OCTET_STRING)
+} ASN1_NDEF_SEQUENCE_END(GOST_KEY_AGREEMENT_INFO)
+
+IMPLEMENT_ASN1_FUNCTIONS(GOST_KEY_AGREEMENT_INFO)
+
+ASN1_NDEF_SEQUENCE(GOST_KEY_PARAMS) = {
+        ASN1_SIMPLE(GOST_KEY_PARAMS, key_params, ASN1_OBJECT),
+        ASN1_SIMPLE(GOST_KEY_PARAMS, hash_params, ASN1_OBJECT),
+        ASN1_OPT(GOST_KEY_PARAMS, cipher_params, ASN1_OBJECT),
+} ASN1_NDEF_SEQUENCE_END(GOST_KEY_PARAMS)
+
+IMPLEMENT_ASN1_FUNCTIONS(GOST_KEY_PARAMS)
+
+ASN1_NDEF_SEQUENCE(GOST_CIPHER_PARAMS) = {
+        ASN1_SIMPLE(GOST_CIPHER_PARAMS, iv, ASN1_OCTET_STRING),
+        ASN1_SIMPLE(GOST_CIPHER_PARAMS, enc_param_set, ASN1_OBJECT),
+} ASN1_NDEF_SEQUENCE_END(GOST_CIPHER_PARAMS)
+
+IMPLEMENT_ASN1_FUNCTIONS(GOST_CIPHER_PARAMS)
+
+ASN1_NDEF_SEQUENCE(GOST_CLIENT_KEY_EXCHANGE_PARAMS) = { /* FIXME incomplete */
+    ASN1_SIMPLE(GOST_CLIENT_KEY_EXCHANGE_PARAMS, gkt, GOST_KEY_TRANSPORT)
+}
+
+ASN1_NDEF_SEQUENCE_END(GOST_CLIENT_KEY_EXCHANGE_PARAMS)
+IMPLEMENT_ASN1_FUNCTIONS(GOST_CLIENT_KEY_EXCHANGE_PARAMS)
diff --git a/gost_crypt.c b/gost_crypt.c
new file mode 100644 (file)
index 0000000..2bbdc6c
--- /dev/null
@@ -0,0 +1,623 @@
+/**********************************************************************
+ *                          gost_crypt.c                              *
+ *             Copyright (c) 2005-2006 Cryptocom LTD                  *
+ *         This file is distributed under the same license as OpenSSL *
+ *                                                                    *
+ *       OpenSSL interface to GOST 28147-89 cipher functions          *
+ *          Requires OpenSSL 0.9.9 for compilation                    *
+ **********************************************************************/
+#include <string.h>
+#include "gost89.h"
+#include <openssl/rand.h>
+#include "e_gost_err.h"
+#include "gost_lcl.h"
+
+#if !defined(CCGOST_DEBUG) && !defined(DEBUG)
+# ifndef NDEBUG
+#  define NDEBUG
+# endif
+#endif
+#include <assert.h>
+
+static int gost_cipher_init(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                            const unsigned char *iv, int enc);
+static int gost_cipher_init_cpa(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                                const unsigned char *iv, int enc);
+/* Handles block of data in CFB mode */
+static int gost_cipher_do_cfb(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                              const unsigned char *in, size_t inl);
+/* Handles block of data in CNT mode */
+static int gost_cipher_do_cnt(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                              const unsigned char *in, size_t inl);
+/* Cleanup function */
+static int gost_cipher_cleanup(EVP_CIPHER_CTX *);
+/* set/get cipher parameters */
+static int gost89_set_asn1_parameters(EVP_CIPHER_CTX *ctx, ASN1_TYPE *params);
+static int gost89_get_asn1_parameters(EVP_CIPHER_CTX *ctx, ASN1_TYPE *params);
+/* Control function */
+static int gost_cipher_ctl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr);
+
+EVP_CIPHER cipher_gost = {
+    NID_id_Gost28147_89,
+    1,                          /* block_size */
+    32,                         /* key_size */
+    8,                          /* iv_len */
+    EVP_CIPH_CFB_MODE | EVP_CIPH_NO_PADDING |
+        EVP_CIPH_CUSTOM_IV | EVP_CIPH_RAND_KEY | EVP_CIPH_ALWAYS_CALL_INIT,
+    gost_cipher_init,
+    gost_cipher_do_cfb,
+    gost_cipher_cleanup,
+    sizeof(struct ossl_gost_cipher_ctx), /* ctx_size */
+    gost89_set_asn1_parameters,
+    gost89_get_asn1_parameters,
+    gost_cipher_ctl,
+    NULL,
+};
+
+EVP_CIPHER cipher_gost_cpacnt = {
+    NID_gost89_cnt,
+    1,                          /* block_size */
+    32,                         /* key_size */
+    8,                          /* iv_len */
+    EVP_CIPH_OFB_MODE | EVP_CIPH_NO_PADDING |
+        EVP_CIPH_CUSTOM_IV | EVP_CIPH_RAND_KEY | EVP_CIPH_ALWAYS_CALL_INIT,
+    gost_cipher_init_cpa,
+    gost_cipher_do_cnt,
+    gost_cipher_cleanup,
+    sizeof(struct ossl_gost_cipher_ctx), /* ctx_size */
+    gost89_set_asn1_parameters,
+    gost89_get_asn1_parameters,
+    gost_cipher_ctl,
+    NULL,
+};
+
+/* Implementation of GOST 28147-89 in MAC (imitovstavka) mode */
+/* Init functions which set specific parameters */
+static int gost_imit_init_cpa(EVP_MD_CTX *ctx);
+/* process block of data */
+static int gost_imit_update(EVP_MD_CTX *ctx, const void *data, size_t count);
+/* Return computed value */
+static int gost_imit_final(EVP_MD_CTX *ctx, unsigned char *md);
+/* Copies context */
+static int gost_imit_copy(EVP_MD_CTX *to, const EVP_MD_CTX *from);
+static int gost_imit_cleanup(EVP_MD_CTX *ctx);
+/* Control function, knows how to set MAC key.*/
+static int gost_imit_ctrl(EVP_MD_CTX *ctx, int type, int arg, void *ptr);
+
+EVP_MD imit_gost_cpa = {
+    NID_id_Gost28147_89_MAC,
+    NID_undef,
+    4,
+    0,
+    gost_imit_init_cpa,
+    gost_imit_update,
+    gost_imit_final,
+    gost_imit_copy,
+    gost_imit_cleanup,
+    NULL,
+    NULL,
+    {0, 0, 0, 0, 0},
+    8,
+    sizeof(struct ossl_gost_imit_ctx),
+    gost_imit_ctrl
+};
+
+/*
+ * Correspondence between gost parameter OIDs and substitution blocks
+ * NID field is filed by register_gost_NID function in engine.c
+ * upon engine initialization
+ */
+
+struct gost_cipher_info gost_cipher_list[] = {
+    /*- NID *//*
+     * Subst block
+     *//*
+     * Key meshing
+     */
+    /*
+     * {NID_id_GostR3411_94_CryptoProParamSet,&GostR3411_94_CryptoProParamSet,0},
+     */
+    {NID_id_Gost28147_89_cc, &GostR3411_94_CryptoProParamSet, 0},
+    {NID_id_Gost28147_89_CryptoPro_A_ParamSet, &Gost28147_CryptoProParamSetA,
+     1},
+    {NID_id_Gost28147_89_CryptoPro_B_ParamSet, &Gost28147_CryptoProParamSetB,
+     1},
+    {NID_id_Gost28147_89_CryptoPro_C_ParamSet, &Gost28147_CryptoProParamSetC,
+     1},
+    {NID_id_Gost28147_89_CryptoPro_D_ParamSet, &Gost28147_CryptoProParamSetD,
+     1},
+    {NID_id_Gost28147_89_TestParamSet, &Gost28147_TestParamSet, 1},
+    {NID_undef, NULL, 0}
+};
+
+/*
+ * get encryption parameters from crypto network settings FIXME For now we
+ * use environment var CRYPT_PARAMS as place to store these settings.
+ * Actually, it is better to use engine control command, read from
+ * configuration file to set them
+ */
+const struct gost_cipher_info *get_encryption_params(ASN1_OBJECT *obj)
+{
+    int nid;
+    struct gost_cipher_info *param;
+    if (!obj) {
+        const char *params = get_gost_engine_param(GOST_PARAM_CRYPT_PARAMS);
+        if (!params || !strlen(params))
+            return &gost_cipher_list[1];
+
+        nid = OBJ_txt2nid(params);
+        if (nid == NID_undef) {
+            GOSTerr(GOST_F_GET_ENCRYPTION_PARAMS,
+                    GOST_R_INVALID_CIPHER_PARAM_OID);
+            return NULL;
+        }
+    } else {
+        nid = OBJ_obj2nid(obj);
+    }
+    for (param = gost_cipher_list; param->sblock != NULL && param->nid != nid;
+         param++) ;
+    if (!param->sblock) {
+        GOSTerr(GOST_F_GET_ENCRYPTION_PARAMS, GOST_R_INVALID_CIPHER_PARAMS);
+        return NULL;
+    }
+    return param;
+}
+
+/* Sets cipher param from paramset NID. */
+static int gost_cipher_set_param(struct ossl_gost_cipher_ctx *c, int nid)
+{
+    const struct gost_cipher_info *param;
+    param =
+        get_encryption_params((nid == NID_undef ? NULL : OBJ_nid2obj(nid)));
+    if (!param)
+        return 0;
+
+    c->paramNID = param->nid;
+    c->key_meshing = param->key_meshing;
+    c->count = 0;
+    gost_init(&(c->cctx), param->sblock);
+    return 1;
+}
+
+/* Initializes EVP_CIPHER_CTX by paramset NID */
+static int gost_cipher_init_param(EVP_CIPHER_CTX *ctx,
+                                  const unsigned char *key,
+                                  const unsigned char *iv, int enc,
+                                  int paramNID, int mode)
+{
+    struct ossl_gost_cipher_ctx *c = ctx->cipher_data;
+    if (ctx->app_data == NULL) {
+        if (!gost_cipher_set_param(c, paramNID))
+            return 0;
+        ctx->app_data = ctx->cipher_data;
+    }
+    if (key)
+        gost_key(&(c->cctx), key);
+    if (iv)
+        memcpy(ctx->oiv, iv, EVP_CIPHER_CTX_iv_length(ctx));
+    memcpy(ctx->iv, ctx->oiv, EVP_CIPHER_CTX_iv_length(ctx));
+    return 1;
+}
+
+static int gost_cipher_init_cpa(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                                const unsigned char *iv, int enc)
+{
+    struct ossl_gost_cipher_ctx *c = ctx->cipher_data;
+    gost_init(&(c->cctx), &Gost28147_CryptoProParamSetA);
+    c->key_meshing = 1;
+    c->count = 0;
+    if (key)
+        gost_key(&(c->cctx), key);
+    if (iv)
+        memcpy(ctx->oiv, iv, EVP_CIPHER_CTX_iv_length(ctx));
+    memcpy(ctx->iv, ctx->oiv, EVP_CIPHER_CTX_iv_length(ctx));
+    return 1;
+}
+
+/* Initializes EVP_CIPHER_CTX with default values */
+int gost_cipher_init(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                     const unsigned char *iv, int enc)
+{
+    return gost_cipher_init_param(ctx, key, iv, enc, NID_undef,
+                                  EVP_CIPH_CFB_MODE);
+}
+
+/*
+ * Wrapper around gostcrypt function from gost89.c which perform key meshing
+ * when nesseccary
+ */
+static void gost_crypt_mesh(void *ctx, unsigned char *iv, unsigned char *buf)
+{
+    struct ossl_gost_cipher_ctx *c = ctx;
+    assert(c->count % 8 == 0 && c->count <= 1024);
+    if (c->key_meshing && c->count == 1024) {
+        cryptopro_key_meshing(&(c->cctx), iv);
+    }
+    gostcrypt(&(c->cctx), iv, buf);
+    c->count = c->count % 1024 + 8;
+}
+
+static void gost_cnt_next(void *ctx, unsigned char *iv, unsigned char *buf)
+{
+    struct ossl_gost_cipher_ctx *c = ctx;
+    word32 g, go;
+    unsigned char buf1[8];
+    assert(c->count % 8 == 0 && c->count <= 1024);
+    if (c->key_meshing && c->count == 1024) {
+        cryptopro_key_meshing(&(c->cctx), iv);
+    }
+    if (c->count == 0) {
+        gostcrypt(&(c->cctx), iv, buf1);
+    } else {
+        memcpy(buf1, iv, 8);
+    }
+    g = buf1[0] | (buf1[1] << 8) | (buf1[2] << 16) | ((word32) buf1[3] << 24);
+    g += 0x01010101;
+    buf1[0] = (unsigned char)(g & 0xff);
+    buf1[1] = (unsigned char)((g >> 8) & 0xff);
+    buf1[2] = (unsigned char)((g >> 16) & 0xff);
+    buf1[3] = (unsigned char)((g >> 24) & 0xff);
+    g = buf1[4] | (buf1[5] << 8) | (buf1[6] << 16) | ((word32) buf1[7] << 24);
+    go = g;
+    g += 0x01010104;
+    if (go > g)                 /* overflow */
+        g++;
+    buf1[4] = (unsigned char)(g & 0xff);
+    buf1[5] = (unsigned char)((g >> 8) & 0xff);
+    buf1[6] = (unsigned char)((g >> 16) & 0xff);
+    buf1[7] = (unsigned char)((g >> 24) & 0xff);
+    memcpy(iv, buf1, 8);
+    gostcrypt(&(c->cctx), buf1, buf);
+    c->count = c->count % 1024 + 8;
+}
+
+/* GOST encryption in CFB mode */
+int gost_cipher_do_cfb(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                       const unsigned char *in, size_t inl)
+{
+    const unsigned char *in_ptr = in;
+    unsigned char *out_ptr = out;
+    size_t i = 0;
+    size_t j = 0;
+/* process partial block if any */
+    if (ctx->num) {
+        for (j = ctx->num, i = 0; j < 8 && i < inl;
+             j++, i++, in_ptr++, out_ptr++) {
+            if (!ctx->encrypt)
+                ctx->buf[j + 8] = *in_ptr;
+            *out_ptr = ctx->buf[j] ^ (*in_ptr);
+            if (ctx->encrypt)
+                ctx->buf[j + 8] = *out_ptr;
+        }
+        if (j == 8) {
+            memcpy(ctx->iv, ctx->buf + 8, 8);
+            ctx->num = 0;
+        } else {
+            ctx->num = j;
+            return 1;
+        }
+    }
+
+    for (; i + 8 < inl; i += 8, in_ptr += 8, out_ptr += 8) {
+        /*
+         * block cipher current iv
+         */
+        gost_crypt_mesh(ctx->cipher_data, ctx->iv, ctx->buf);
+        /*
+         * xor next block of input text with it and output it
+         */
+        /*
+         * output this block
+         */
+        if (!ctx->encrypt)
+            memcpy(ctx->iv, in_ptr, 8);
+        for (j = 0; j < 8; j++) {
+            out_ptr[j] = ctx->buf[j] ^ in_ptr[j];
+        }
+        /* Encrypt */
+        /* Next iv is next block of cipher text */
+        if (ctx->encrypt)
+            memcpy(ctx->iv, out_ptr, 8);
+    }
+/* Process rest of buffer */
+    if (i < inl) {
+        gost_crypt_mesh(ctx->cipher_data, ctx->iv, ctx->buf);
+        if (!ctx->encrypt)
+            memcpy(ctx->buf + 8, in_ptr, inl - i);
+        for (j = 0; i < inl; j++, i++) {
+            out_ptr[j] = ctx->buf[j] ^ in_ptr[j];
+        }
+        ctx->num = j;
+        if (ctx->encrypt)
+            memcpy(ctx->buf + 8, out_ptr, j);
+    } else {
+        ctx->num = 0;
+    }
+    return 1;
+}
+
+static int gost_cipher_do_cnt(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                              const unsigned char *in, size_t inl)
+{
+    const unsigned char *in_ptr = in;
+    unsigned char *out_ptr = out;
+    size_t i = 0;
+    size_t j;
+/* process partial block if any */
+    if (ctx->num) {
+        for (j = ctx->num, i = 0; j < 8 && i < inl;
+             j++, i++, in_ptr++, out_ptr++) {
+            *out_ptr = ctx->buf[j] ^ (*in_ptr);
+        }
+        if (j == 8) {
+            ctx->num = 0;
+        } else {
+            ctx->num = j;
+            return 1;
+        }
+    }
+
+    for (; i + 8 < inl; i += 8, in_ptr += 8, out_ptr += 8) {
+        /*
+         * block cipher current iv
+         */
+        /* Encrypt */
+        gost_cnt_next(ctx->cipher_data, ctx->iv, ctx->buf);
+        /*
+         * xor next block of input text with it and output it
+         */
+        /*
+         * output this block
+         */
+        for (j = 0; j < 8; j++) {
+            out_ptr[j] = ctx->buf[j] ^ in_ptr[j];
+        }
+    }
+/* Process rest of buffer */
+    if (i < inl) {
+        gost_cnt_next(ctx->cipher_data, ctx->iv, ctx->buf);
+        for (j = 0; i < inl; j++, i++) {
+            out_ptr[j] = ctx->buf[j] ^ in_ptr[j];
+        }
+        ctx->num = j;
+    } else {
+        ctx->num = 0;
+    }
+    return 1;
+}
+
+/* Cleaning up of EVP_CIPHER_CTX */
+int gost_cipher_cleanup(EVP_CIPHER_CTX *ctx)
+{
+    gost_destroy(&((struct ossl_gost_cipher_ctx *)ctx->cipher_data)->cctx);
+    ctx->app_data = NULL;
+    return 1;
+}
+
+/* Control function for gost cipher */
+int gost_cipher_ctl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr)
+{
+    switch (type) {
+    case EVP_CTRL_RAND_KEY:
+        {
+            if (RAND_bytes((unsigned char *)ptr, ctx->key_len) <= 0) {
+                GOSTerr(GOST_F_GOST_CIPHER_CTL,
+                        GOST_R_RANDOM_GENERATOR_ERROR);
+                return -1;
+            }
+            break;
+        }
+    case EVP_CTRL_PBE_PRF_NID:
+        if (ptr) {
+            *((int *)ptr) = NID_id_HMACGostR3411_94;
+            return 1;
+        } else {
+            return 0;
+        }
+
+    default:
+        GOSTerr(GOST_F_GOST_CIPHER_CTL,
+                GOST_R_UNSUPPORTED_CIPHER_CTL_COMMAND);
+        return -1;
+    }
+    return 1;
+}
+
+/* Set cipher parameters from ASN1 structure */
+int gost89_set_asn1_parameters(EVP_CIPHER_CTX *ctx, ASN1_TYPE *params)
+{
+    int len = 0;
+    unsigned char *buf = NULL;
+    unsigned char *p = NULL;
+    struct ossl_gost_cipher_ctx *c = ctx->cipher_data;
+    GOST_CIPHER_PARAMS *gcp = GOST_CIPHER_PARAMS_new();
+    ASN1_OCTET_STRING *os = NULL;
+    if (!gcp) {
+        GOSTerr(GOST_F_GOST89_SET_ASN1_PARAMETERS, GOST_R_NO_MEMORY);
+        return 0;
+    }
+    if (!ASN1_OCTET_STRING_set(gcp->iv, ctx->iv, ctx->cipher->iv_len)) {
+        GOST_CIPHER_PARAMS_free(gcp);
+        GOSTerr(GOST_F_GOST89_SET_ASN1_PARAMETERS, GOST_R_NO_MEMORY);
+        return 0;
+    }
+    ASN1_OBJECT_free(gcp->enc_param_set);
+    gcp->enc_param_set = OBJ_nid2obj(c->paramNID);
+
+    len = i2d_GOST_CIPHER_PARAMS(gcp, NULL);
+    p = buf = (unsigned char *)OPENSSL_malloc(len);
+    if (!buf) {
+        GOST_CIPHER_PARAMS_free(gcp);
+        GOSTerr(GOST_F_GOST89_SET_ASN1_PARAMETERS, GOST_R_NO_MEMORY);
+        return 0;
+    }
+    i2d_GOST_CIPHER_PARAMS(gcp, &p);
+    GOST_CIPHER_PARAMS_free(gcp);
+
+    os = ASN1_OCTET_STRING_new();
+
+    if (!os || !ASN1_OCTET_STRING_set(os, buf, len)) {
+        OPENSSL_free(buf);
+        GOSTerr(GOST_F_GOST89_SET_ASN1_PARAMETERS, GOST_R_NO_MEMORY);
+        return 0;
+    }
+    OPENSSL_free(buf);
+
+    ASN1_TYPE_set(params, V_ASN1_SEQUENCE, os);
+    return 1;
+}
+
+/* Store parameters into ASN1 structure */
+int gost89_get_asn1_parameters(EVP_CIPHER_CTX *ctx, ASN1_TYPE *params)
+{
+    int ret = -1;
+    int len;
+    GOST_CIPHER_PARAMS *gcp = NULL;
+    unsigned char *p;
+    struct ossl_gost_cipher_ctx *c = ctx->cipher_data;
+    if (ASN1_TYPE_get(params) != V_ASN1_SEQUENCE) {
+        return ret;
+    }
+
+    p = params->value.sequence->data;
+
+    gcp = d2i_GOST_CIPHER_PARAMS(NULL, (const unsigned char **)&p,
+                                 params->value.sequence->length);
+
+    len = gcp->iv->length;
+    if (len != ctx->cipher->iv_len) {
+        GOST_CIPHER_PARAMS_free(gcp);
+        GOSTerr(GOST_F_GOST89_GET_ASN1_PARAMETERS, GOST_R_INVALID_IV_LENGTH);
+        return -1;
+    }
+    if (!gost_cipher_set_param(c, OBJ_obj2nid(gcp->enc_param_set))) {
+        GOST_CIPHER_PARAMS_free(gcp);
+        return -1;
+    }
+    memcpy(ctx->oiv, gcp->iv->data, len);
+
+    GOST_CIPHER_PARAMS_free(gcp);
+
+    return 1;
+}
+
+int gost_imit_init_cpa(EVP_MD_CTX *ctx)
+{
+    struct ossl_gost_imit_ctx *c = ctx->md_data;
+    memset(c->buffer, 0, sizeof(c->buffer));
+    memset(c->partial_block, 0, sizeof(c->partial_block));
+    c->count = 0;
+    c->bytes_left = 0;
+    c->key_meshing = 1;
+    gost_init(&(c->cctx), &Gost28147_CryptoProParamSetA);
+    return 1;
+}
+
+static void mac_block_mesh(struct ossl_gost_imit_ctx *c,
+                           const unsigned char *data)
+{
+    unsigned char buffer[8];
+    /*
+     * We are using local buffer for iv because CryptoPro doesn't interpret
+     * internal state of MAC algorithm as iv during keymeshing (but does
+     * initialize internal state from iv in key transport
+     */
+    assert(c->count % 8 == 0 && c->count <= 1024);
+    if (c->key_meshing && c->count == 1024) {
+        cryptopro_key_meshing(&(c->cctx), buffer);
+    }
+    mac_block(&(c->cctx), c->buffer, data);
+    c->count = c->count % 1024 + 8;
+}
+
+int gost_imit_update(EVP_MD_CTX *ctx, const void *data, size_t count)
+{
+    struct ossl_gost_imit_ctx *c = ctx->md_data;
+    const unsigned char *p = data;
+    size_t bytes = count, i;
+    if (!(c->key_set)) {
+        GOSTerr(GOST_F_GOST_IMIT_UPDATE, GOST_R_MAC_KEY_NOT_SET);
+        return 0;
+    }
+    if (c->bytes_left) {
+        for (i = c->bytes_left; i < 8 && bytes > 0; bytes--, i++, p++) {
+            c->partial_block[i] = *p;
+        }
+        if (i == 8) {
+            mac_block_mesh(c, c->partial_block);
+        } else {
+            c->bytes_left = i;
+            return 1;
+        }
+    }
+    while (bytes > 8) {
+        mac_block_mesh(c, p);
+        p += 8;
+        bytes -= 8;
+    }
+    if (bytes > 0) {
+        memcpy(c->partial_block, p, bytes);
+    }
+    c->bytes_left = bytes;
+    return 1;
+}
+
+int gost_imit_final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+    struct ossl_gost_imit_ctx *c = ctx->md_data;
+    if (!c->key_set) {
+        GOSTerr(GOST_F_GOST_IMIT_FINAL, GOST_R_MAC_KEY_NOT_SET);
+        return 0;
+    }
+    if (c->count == 0 && c->bytes_left) {
+        unsigned char buffer[8];
+        memset(buffer, 0, 8);
+        gost_imit_update(ctx, buffer, 8);
+    }
+    if (c->bytes_left) {
+        int i;
+        for (i = c->bytes_left; i < 8; i++) {
+            c->partial_block[i] = 0;
+        }
+        mac_block_mesh(c, c->partial_block);
+    }
+    get_mac(c->buffer, 32, md);
+    return 1;
+}
+
+int gost_imit_ctrl(EVP_MD_CTX *ctx, int type, int arg, void *ptr)
+{
+    switch (type) {
+    case EVP_MD_CTRL_KEY_LEN:
+        *((unsigned int *)(ptr)) = 32;
+        return 1;
+    case EVP_MD_CTRL_SET_KEY:
+        {
+            if (arg != 32) {
+                GOSTerr(GOST_F_GOST_IMIT_CTRL, GOST_R_INVALID_MAC_KEY_LENGTH);
+                return 0;
+            }
+
+            gost_key(&(((struct ossl_gost_imit_ctx *)(ctx->md_data))->cctx),
+                     ptr);
+            ((struct ossl_gost_imit_ctx *)(ctx->md_data))->key_set = 1;
+            return 1;
+
+        }
+    default:
+        return 0;
+    }
+}
+
+int gost_imit_copy(EVP_MD_CTX *to, const EVP_MD_CTX *from)
+{
+    memcpy(to->md_data, from->md_data, sizeof(struct ossl_gost_imit_ctx));
+    return 1;
+}
+
+/* Clean up imit ctx */
+int gost_imit_cleanup(EVP_MD_CTX *ctx)
+{
+    memset(ctx->md_data, 0, sizeof(struct ossl_gost_imit_ctx));
+    return 1;
+}
diff --git a/gost_ctl.c b/gost_ctl.c
new file mode 100644 (file)
index 0000000..6c93c45
--- /dev/null
@@ -0,0 +1,93 @@
+/**********************************************************************
+ *                        gost_ctl.c                                  *
+ *             Copyright (c) 2005-2006 Cryptocom LTD                  *
+ *       This file is distributed under the same license as OpenSSL   *
+ *                                                                    *
+ *        Implementation of control commands for GOST engine          *
+ *            OpenSSL 0.9.9 libraries required                        *
+ **********************************************************************/
+#include <stdlib.h>
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/engine.h>
+#include <openssl/buffer.h>
+#include "gost_lcl.h"
+
+static char *gost_params[GOST_PARAM_MAX + 1] = { NULL };
+static const char *gost_envnames[] = { "CRYPT_PARAMS" };
+
+const ENGINE_CMD_DEFN gost_cmds[] = {
+/*- { GOST_CTRL_RNG,
+    "RNG",
+    "Type of random number generator to use",
+    ENGINE_CMD_FLAG_STRING
+    },
+    { GOST_CTRL_RNG_PARAMS,
+    "RNG_PARAMS",
+    "Parameter for random number generator",
+    ENGINE_CMD_FLAG_STRING
+    },
+*/ {GOST_CTRL_CRYPT_PARAMS,
+           "CRYPT_PARAMS",
+           "OID of default GOST 28147-89 parameters",
+           ENGINE_CMD_FLAG_STRING},
+    {0, NULL, NULL, 0}
+};
+
+void gost_param_free()
+{
+    int i;
+    for (i = 0; i <= GOST_PARAM_MAX; i++)
+        if (gost_params[i] != NULL) {
+            OPENSSL_free(gost_params[i]);
+            gost_params[i] = NULL;
+        }
+
+}
+
+int gost_control_func(ENGINE *e, int cmd, long i, void *p, void (*f) (void))
+{
+    int param = cmd - ENGINE_CMD_BASE;
+    int ret = 0;
+    if (param < 0 || param > GOST_PARAM_MAX)
+        return -1;
+    ret = gost_set_default_param(param, p);
+    return ret;
+}
+
+const char *get_gost_engine_param(int param)
+{
+    char *tmp;
+    if (param < 0 || param > GOST_PARAM_MAX)
+        return NULL;
+    if (gost_params[param] != NULL) {
+        return gost_params[param];
+    }
+    tmp = getenv(gost_envnames[param]);
+    if (tmp) {
+        if (gost_params[param])
+            OPENSSL_free(gost_params[param]);
+        gost_params[param] = BUF_strdup(tmp);
+        return gost_params[param];
+    }
+    return NULL;
+}
+
+int gost_set_default_param(int param, const char *value)
+{
+    const char *tmp;
+    if (param < 0 || param > GOST_PARAM_MAX)
+        return 0;
+    tmp = getenv(gost_envnames[param]);
+    /*
+     * if there is value in the environment, use it, else -passed string *
+     */
+    if (!tmp)
+        tmp = value;
+    if (gost_params[param])
+        OPENSSL_free(gost_params[param]);
+    gost_params[param] = BUF_strdup(tmp);
+
+    return 1;
+}
diff --git a/gost_eng.c b/gost_eng.c
new file mode 100644 (file)
index 0000000..5924791
--- /dev/null
@@ -0,0 +1,281 @@
+/**********************************************************************
+ *                          gost_eng.c                                *
+ *             Copyright (c) 2005-2006 Cryptocom LTD                  *
+ *         This file is distributed under the same license as OpenSSL *
+ *                                                                    *
+ *              Main file of GOST engine                              *
+ *       for OpenSSL                                                  *
+ *          Requires OpenSSL 0.9.9 for compilation                    *
+ **********************************************************************/
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/engine.h>
+#include <openssl/obj_mac.h>
+#include "e_gost_err.h"
+#include "gost_lcl.h"
+static const char *engine_gost_id = "gost";
+static const char *engine_gost_name =
+    "Reference implementation of GOST engine";
+
+/* Symmetric cipher and digest function registrar */
+
+static int gost_ciphers(ENGINE *e, const EVP_CIPHER **cipher,
+                        const int **nids, int nid);
+
+static int gost_digests(ENGINE *e, const EVP_MD **digest,
+                        const int **nids, int ind);
+
+static int gost_pkey_meths(ENGINE *e, EVP_PKEY_METHOD **pmeth,
+                           const int **nids, int nid);
+
+static int gost_pkey_asn1_meths(ENGINE *e, EVP_PKEY_ASN1_METHOD **ameth,
+                                const int **nids, int nid);
+
+static int gost_cipher_nids[] = { NID_id_Gost28147_89, NID_gost89_cnt, 0 };
+
+static int gost_digest_nids[] =
+    { NID_id_GostR3411_94, NID_id_Gost28147_89_MAC, 0 };
+
+static int gost_pkey_meth_nids[] = { NID_id_GostR3410_94,
+    NID_id_GostR3410_2001, NID_id_Gost28147_89_MAC, 0
+};
+
+static EVP_PKEY_METHOD *pmeth_GostR3410_94 = NULL,
+    *pmeth_GostR3410_2001 = NULL, *pmeth_Gost28147_MAC = NULL;
+
+static EVP_PKEY_ASN1_METHOD *ameth_GostR3410_94 = NULL,
+    *ameth_GostR3410_2001 = NULL, *ameth_Gost28147_MAC = NULL;
+
+static int gost_engine_init(ENGINE *e)
+{
+    return 1;
+}
+
+static int gost_engine_finish(ENGINE *e)
+{
+    return 1;
+}
+
+static int gost_engine_destroy(ENGINE *e)
+{
+    gost_param_free();
+
+    pmeth_GostR3410_94 = NULL;
+    pmeth_GostR3410_2001 = NULL;
+    pmeth_Gost28147_MAC = NULL;
+    ameth_GostR3410_94 = NULL;
+    ameth_GostR3410_2001 = NULL;
+    ameth_Gost28147_MAC = NULL;
+    return 1;
+}
+
+static int bind_gost(ENGINE *e, const char *id)
+{
+    int ret = 0;
+    if (id && strcmp(id, engine_gost_id))
+        return 0;
+    if (ameth_GostR3410_94) {
+        printf("GOST engine already loaded\n");
+        goto end;
+    }
+
+    if (!ENGINE_set_id(e, engine_gost_id)) {
+        printf("ENGINE_set_id failed\n");
+        goto end;
+    }
+    if (!ENGINE_set_name(e, engine_gost_name)) {
+        printf("ENGINE_set_name failed\n");
+        goto end;
+    }
+    if (!ENGINE_set_digests(e, gost_digests)) {
+        printf("ENGINE_set_digests failed\n");
+        goto end;
+    }
+    if (!ENGINE_set_ciphers(e, gost_ciphers)) {
+        printf("ENGINE_set_ciphers failed\n");
+        goto end;
+    }
+    if (!ENGINE_set_pkey_meths(e, gost_pkey_meths)) {
+        printf("ENGINE_set_pkey_meths failed\n");
+        goto end;
+    }
+    if (!ENGINE_set_pkey_asn1_meths(e, gost_pkey_asn1_meths)) {
+        printf("ENGINE_set_pkey_asn1_meths failed\n");
+        goto end;
+    }
+    /* Control function and commands */
+    if (!ENGINE_set_cmd_defns(e, gost_cmds)) {
+        fprintf(stderr, "ENGINE_set_cmd_defns failed\n");
+        goto end;
+    }
+    if (!ENGINE_set_ctrl_function(e, gost_control_func)) {
+        fprintf(stderr, "ENGINE_set_ctrl_func failed\n");
+        goto end;
+    }
+    if (!ENGINE_set_destroy_function(e, gost_engine_destroy)
+        || !ENGINE_set_init_function(e, gost_engine_init)
+        || !ENGINE_set_finish_function(e, gost_engine_finish)) {
+        goto end;
+    }
+
+    if (!register_ameth_gost
+        (NID_id_GostR3410_94, &ameth_GostR3410_94, "GOST94",
+         "GOST R 34.10-94"))
+        goto end;
+    if (!register_ameth_gost
+        (NID_id_GostR3410_2001, &ameth_GostR3410_2001, "GOST2001",
+         "GOST R 34.10-2001"))
+        goto end;
+    if (!register_ameth_gost(NID_id_Gost28147_89_MAC, &ameth_Gost28147_MAC,
+                             "GOST-MAC", "GOST 28147-89 MAC"))
+        goto end;
+
+    if (!register_pmeth_gost(NID_id_GostR3410_94, &pmeth_GostR3410_94, 0))
+        goto end;
+    if (!register_pmeth_gost(NID_id_GostR3410_2001, &pmeth_GostR3410_2001, 0))
+        goto end;
+    if (!register_pmeth_gost
+        (NID_id_Gost28147_89_MAC, &pmeth_Gost28147_MAC, 0))
+        goto end;
+    if (!ENGINE_register_ciphers(e)
+        || !ENGINE_register_digests(e)
+        || !ENGINE_register_pkey_meths(e)
+        /* These two actually should go in LIST_ADD command */
+        || !EVP_add_cipher(&cipher_gost)
+        || !EVP_add_cipher(&cipher_gost_cpacnt)
+        || !EVP_add_digest(&digest_gost)
+        || !EVP_add_digest(&imit_gost_cpa)
+        ) {
+        goto end;
+    }
+
+    ERR_load_GOST_strings();
+    ret = 1;
+ end:
+    return ret;
+}
+
+#ifndef OPENSSL_NO_DYNAMIC_ENGINE
+IMPLEMENT_DYNAMIC_BIND_FN(bind_gost)
+    IMPLEMENT_DYNAMIC_CHECK_FN()
+#endif                          /* ndef OPENSSL_NO_DYNAMIC_ENGINE */
+static int gost_digests(ENGINE *e, const EVP_MD **digest,
+                        const int **nids, int nid)
+{
+    int ok = 1;
+    if (!digest) {
+        *nids = gost_digest_nids;
+        return 2;
+    }
+    /*
+     * printf("Digest no %d requested\n",nid);
+     */
+    if (nid == NID_id_GostR3411_94) {
+        *digest = &digest_gost;
+    } else if (nid == NID_id_Gost28147_89_MAC) {
+        *digest = &imit_gost_cpa;
+    } else {
+        ok = 0;
+        *digest = NULL;
+    }
+    return ok;
+}
+
+static int gost_ciphers(ENGINE *e, const EVP_CIPHER **cipher,
+                        const int **nids, int nid)
+{
+    int ok = 1;
+    if (!cipher) {
+        *nids = gost_cipher_nids;
+        return 2;               /* two ciphers are supported */
+    }
+
+    if (nid == NID_id_Gost28147_89) {
+        *cipher = &cipher_gost;
+    } else if (nid == NID_gost89_cnt) {
+        *cipher = &cipher_gost_cpacnt;
+    } else {
+        ok = 0;
+        *cipher = NULL;
+    }
+    return ok;
+}
+
+static int gost_pkey_meths(ENGINE *e, EVP_PKEY_METHOD **pmeth,
+                           const int **nids, int nid)
+{
+    if (!pmeth) {
+        *nids = gost_pkey_meth_nids;
+        return 3;
+    }
+
+    switch (nid) {
+    case NID_id_GostR3410_94:
+        *pmeth = pmeth_GostR3410_94;
+        return 1;
+    case NID_id_GostR3410_2001:
+        *pmeth = pmeth_GostR3410_2001;
+        return 1;
+    case NID_id_Gost28147_89_MAC:
+        *pmeth = pmeth_Gost28147_MAC;
+        return 1;
+    default:;
+    }
+
+    *pmeth = NULL;
+    return 0;
+}
+
+static int gost_pkey_asn1_meths(ENGINE *e, EVP_PKEY_ASN1_METHOD **ameth,
+                                const int **nids, int nid)
+{
+    if (!ameth) {
+        *nids = gost_pkey_meth_nids;
+        return 3;
+    }
+    switch (nid) {
+    case NID_id_GostR3410_94:
+        *ameth = ameth_GostR3410_94;
+        return 1;
+    case NID_id_GostR3410_2001:
+        *ameth = ameth_GostR3410_2001;
+        return 1;
+    case NID_id_Gost28147_89_MAC:
+        *ameth = ameth_Gost28147_MAC;
+        return 1;
+
+    default:;
+    }
+
+    *ameth = NULL;
+    return 0;
+}
+
+#ifdef OPENSSL_NO_DYNAMIC_ENGINE
+static ENGINE *engine_gost(void)
+{
+    ENGINE *ret = ENGINE_new();
+    if (!ret)
+        return NULL;
+    if (!bind_gost(ret, engine_gost_id)) {
+        ENGINE_free(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+void ENGINE_load_gost(void)
+{
+    ENGINE *toadd;
+    if (pmeth_GostR3410_94)
+        return;
+    toadd = engine_gost();
+    if (!toadd)
+        return;
+    ENGINE_add(toadd);
+    ENGINE_free(toadd);
+    ERR_clear_error();
+}
+#endif
diff --git a/gost_keywrap.c b/gost_keywrap.c
new file mode 100644 (file)
index 0000000..502a83c
--- /dev/null
@@ -0,0 +1,106 @@
+/**********************************************************************
+ *                          keywrap.c                                 *
+ *             Copyright (c) 2005-2006 Cryptocom LTD                  *
+ *         This file is distributed under the same license as OpenSSL *
+ *                                                                    *
+ * Implementation of CryptoPro key wrap algorithm, as defined in      *
+ *               RFC 4357 p 6.3 and 6.4                               *
+ *                  Doesn't need OpenSSL                              *
+ **********************************************************************/
+#include <string.h>
+#include "gost89.h"
+#include "gost_keywrap.h"
+
+/*-
+ * Diversifies key using random UserKey Material
+ * Implements RFC 4357 p 6.5 key diversification algorithm
+ *
+ * inputKey - 32byte key to be diversified
+ * ukm - 8byte user key material
+ * outputKey - 32byte buffer to store diversified key
+ *
+ */
+void keyDiversifyCryptoPro(gost_ctx * ctx, const unsigned char *inputKey,
+                           const unsigned char *ukm, unsigned char *outputKey)
+{
+
+    u4 k, s1, s2;
+    int i, j, mask;
+    unsigned char S[8];
+    memcpy(outputKey, inputKey, 32);
+    for (i = 0; i < 8; i++) {
+        /* Make array of integers from key */
+        /* Compute IV S */
+        s1 = 0, s2 = 0;
+        for (j = 0, mask = 1; j < 8; j++, mask <<= 1) {
+            k = ((u4) outputKey[4 * j]) | (outputKey[4 * j + 1] << 8) |
+                (outputKey[4 * j + 2] << 16) | (outputKey[4 * j + 3] << 24);
+            if (mask & ukm[i]) {
+                s1 += k;
+            } else {
+                s2 += k;
+            }
+        }
+        S[0] = (unsigned char)(s1 & 0xff);
+        S[1] = (unsigned char)((s1 >> 8) & 0xff);
+        S[2] = (unsigned char)((s1 >> 16) & 0xff);
+        S[3] = (unsigned char)((s1 >> 24) & 0xff);
+        S[4] = (unsigned char)(s2 & 0xff);
+        S[5] = (unsigned char)((s2 >> 8) & 0xff);
+        S[6] = (unsigned char)((s2 >> 16) & 0xff);
+        S[7] = (unsigned char)((s2 >> 24) & 0xff);
+        gost_key(ctx, outputKey);
+        gost_enc_cfb(ctx, S, outputKey, outputKey, 4);
+    }
+}
+
+/*-
+ * Wraps key using RFC 4357 6.3
+ * ctx - gost encryption context, initialized with some S-boxes
+ * keyExchangeKey (KEK) 32-byte (256-bit) shared key
+ * ukm - 8 byte (64 bit) user key material,
+ * sessionKey - 32-byte (256-bit) key to be wrapped
+ * wrappedKey - 44-byte buffer to store wrapped key
+ */
+
+int keyWrapCryptoPro(gost_ctx * ctx, const unsigned char *keyExchangeKey,
+                     const unsigned char *ukm,
+                     const unsigned char *sessionKey,
+                     unsigned char *wrappedKey)
+{
+    unsigned char kek_ukm[32];
+    keyDiversifyCryptoPro(ctx, keyExchangeKey, ukm, kek_ukm);
+    gost_key(ctx, kek_ukm);
+    memcpy(wrappedKey, ukm, 8);
+    gost_enc(ctx, sessionKey, wrappedKey + 8, 4);
+    gost_mac_iv(ctx, 32, ukm, sessionKey, 32, wrappedKey + 40);
+    return 1;
+}
+
+/*-
+ * Unwraps key using RFC 4357 6.4
+ * ctx - gost encryption context, initialized with some S-boxes
+ * keyExchangeKey 32-byte shared key
+ * wrappedKey  44 byte key to be unwrapped (concatenation of 8-byte UKM,
+ * 32 byte  encrypted key and 4 byte MAC
+ *
+ * sessionKEy - 32byte buffer to store sessionKey in
+ * Returns 1 if key is decrypted successfully, and 0 if MAC doesn't match
+ */
+
+int keyUnwrapCryptoPro(gost_ctx * ctx, const unsigned char *keyExchangeKey,
+                       const unsigned char *wrappedKey,
+                       unsigned char *sessionKey)
+{
+    unsigned char kek_ukm[32], cek_mac[4];
+    keyDiversifyCryptoPro(ctx, keyExchangeKey, wrappedKey
+                          /* First 8 bytes of wrapped Key is ukm */
+                          , kek_ukm);
+    gost_key(ctx, kek_ukm);
+    gost_dec(ctx, wrappedKey + 8, sessionKey, 4);
+    gost_mac_iv(ctx, 32, wrappedKey, sessionKey, 32, cek_mac);
+    if (memcmp(cek_mac, wrappedKey + 40, 4)) {
+        return 0;
+    }
+    return 1;
+}
diff --git a/gost_keywrap.h b/gost_keywrap.h
new file mode 100644 (file)
index 0000000..7def4c9
--- /dev/null
@@ -0,0 +1,56 @@
+/**********************************************************************
+ *                         gost_keywrap.h                             *
+ *             Copyright (c) 2005-2006 Cryptocom LTD                  *
+ *       This file is distributed under the same license as OpenSSL   *
+ *                                                                    *
+ * Implementation of CryptoPro key wrap algorithm, as defined in      *
+ * RFC 4357 p 6.3 and 6.4                                             *
+ * Doesn't need OpenSSL                                               *
+ **********************************************************************/
+#ifndef GOST_KEYWRAP_H
+# define GOST_KEYWRAP_H
+# include <string.h>
+# include "gost89.h"
+/*-
+ * Diversifies key using random UserKey Material
+ * Implements RFC 4357 p 6.5 key diversification algorithm
+ *
+ * inputKey - 32byte key to be diversified
+ * ukm - 8byte user key material
+ * outputKey - 32byte buffer to store diversified key
+ *
+ */
+void keyDiversifyCryptoPro(gost_ctx * ctx,
+                           const unsigned char *inputKey,
+                           const unsigned char *ukm,
+                           unsigned char *outputKey);
+/*-
+ * Wraps key using RFC 4357 6.3
+ * ctx - gost encryption context, initialized with some S-boxes
+ * keyExchangeKey (KEK) 32-byte (256-bit) shared key
+ * ukm - 8 byte (64 bit) user key material,
+ * sessionKey - 32-byte (256-bit) key to be wrapped
+ * wrappedKey - 44-byte buffer to store wrapped key
+ */
+
+int keyWrapCryptoPro(gost_ctx * ctx,
+                     const unsigned char *keyExchangeKey,
+                     const unsigned char *ukm,
+                     const unsigned char *sessionKey,
+                     unsigned char *wrappedKey);
+/*-
+ * Unwraps key using RFC 4357 6.4
+ * ctx - gost encryption context, initialized with some S-boxes
+ * keyExchangeKey 32-byte shared key
+ * wrappedKey  44 byte key to be unwrapped (concatenation of 8-byte UKM,
+ * 32 byte  encrypted key and 4 byte MAC
+ *
+ * sessionKEy - 32byte buffer to store sessionKey in
+ * Returns 1 if key is decrypted successfully, and 0 if MAC doesn't match
+ */
+
+int keyUnwrapCryptoPro(gost_ctx * ctx,
+                       const unsigned char *keyExchangeKey,
+                       const unsigned char *wrappedKey,
+                       unsigned char *sessionKey);
+#endif
diff --git a/gost_lcl.h b/gost_lcl.h
new file mode 100644 (file)
index 0000000..3a2c7d5
--- /dev/null
@@ -0,0 +1,229 @@
+#ifndef GOST_TOOLS_H
+# define GOST_TOOLS_H
+/**********************************************************************
+ *                        gost_lcl.h                                  *
+ *             Copyright (c) 2006 Cryptocom LTD                       *
+ *       This file is distributed under the same license as OpenSSL   *
+ *                                                                    *
+ *         Internal declarations  used in GOST engine                *
+ *         OpenSSL 0.9.9 libraries required to compile and use        *
+ *                              this code                             *
+ **********************************************************************/
+# include <openssl/bn.h>
+# include <openssl/evp.h>
+# include <openssl/dsa.h>
+# include <openssl/asn1t.h>
+# include <openssl/x509.h>
+# include <openssl/engine.h>
+# include <openssl/ec.h>
+# include "gost89.h"
+# include "gosthash.h"
+/* Control commands */
+# define GOST_PARAM_CRYPT_PARAMS 0
+# define GOST_PARAM_MAX 0
+# define GOST_CTRL_CRYPT_PARAMS (ENGINE_CMD_BASE+GOST_PARAM_CRYPT_PARAMS)
+
+extern const ENGINE_CMD_DEFN gost_cmds[];
+int gost_control_func(ENGINE *e, int cmd, long i, void *p, void (*f) (void));
+const char *get_gost_engine_param(int param);
+int gost_set_default_param(int param, const char *value);
+void gost_param_free(void);
+
+/* method registration */
+
+int register_ameth_gost(int nid, EVP_PKEY_ASN1_METHOD **ameth,
+                        const char *pemstr, const char *info);
+int register_pmeth_gost(int id, EVP_PKEY_METHOD **pmeth, int flags);
+
+/* Gost-specific pmeth control-function parameters */
+/* For GOST R34.10 parameters */
+# define param_ctrl_string "paramset"
+# define EVP_PKEY_CTRL_GOST_PARAMSET (EVP_PKEY_ALG_CTRL+1)
+/* For GOST 28147 MAC */
+# define key_ctrl_string "key"
+# define hexkey_ctrl_string "hexkey"
+# define EVP_PKEY_CTRL_GOST_MAC_HEXKEY (EVP_PKEY_ALG_CTRL+3)
+/* Pmeth internal representation */
+struct gost_pmeth_data {
+    int sign_param_nid;         /* Should be set whenever parameters are
+                                 * filled */
+    EVP_MD *md;
+    unsigned char *shared_ukm;
+    int peer_key_used;
+};
+
+struct gost_mac_pmeth_data {
+    int key_set;
+    EVP_MD *md;
+    unsigned char key[32];
+};
+/* GOST-specific ASN1 structures */
+
+typedef struct {
+    ASN1_OCTET_STRING *encrypted_key;
+    ASN1_OCTET_STRING *imit;
+} GOST_KEY_INFO;
+
+DECLARE_ASN1_FUNCTIONS(GOST_KEY_INFO)
+
+typedef struct {
+    ASN1_OBJECT *cipher;
+    X509_PUBKEY *ephem_key;
+    ASN1_OCTET_STRING *eph_iv;
+} GOST_KEY_AGREEMENT_INFO;
+
+DECLARE_ASN1_FUNCTIONS(GOST_KEY_AGREEMENT_INFO)
+
+typedef struct {
+    GOST_KEY_INFO *key_info;
+    GOST_KEY_AGREEMENT_INFO *key_agreement_info;
+} GOST_KEY_TRANSPORT;
+
+DECLARE_ASN1_FUNCTIONS(GOST_KEY_TRANSPORT)
+
+typedef struct {                /* FIXME incomplete */
+    GOST_KEY_TRANSPORT *gkt;
+} GOST_CLIENT_KEY_EXCHANGE_PARAMS;
+
+/*
+ * Hacks to shorten symbols to 31 characters or less, or OpenVMS. This mimics
+ * what's done in symhacks.h, but since this is a very local header file, I
+ * prefered to put this hack directly here. -- Richard Levitte
+ */
+# ifdef OPENSSL_SYS_VMS
+#  undef GOST_CLIENT_KEY_EXCHANGE_PARAMS_it
+#  define GOST_CLIENT_KEY_EXCHANGE_PARAMS_it      GOST_CLIENT_KEY_EXC_PARAMS_it
+#  undef GOST_CLIENT_KEY_EXCHANGE_PARAMS_new
+#  define GOST_CLIENT_KEY_EXCHANGE_PARAMS_new     GOST_CLIENT_KEY_EXC_PARAMS_new
+#  undef GOST_CLIENT_KEY_EXCHANGE_PARAMS_free
+#  define GOST_CLIENT_KEY_EXCHANGE_PARAMS_free    GOST_CLIENT_KEY_EXC_PARAMS_free
+#  undef d2i_GOST_CLIENT_KEY_EXCHANGE_PARAMS
+#  define d2i_GOST_CLIENT_KEY_EXCHANGE_PARAMS     d2i_GOST_CLIENT_KEY_EXC_PARAMS
+#  undef i2d_GOST_CLIENT_KEY_EXCHANGE_PARAMS
+#  define i2d_GOST_CLIENT_KEY_EXCHANGE_PARAMS     i2d_GOST_CLIENT_KEY_EXC_PARAMS
+# endif                         /* End of hack */
+DECLARE_ASN1_FUNCTIONS(GOST_CLIENT_KEY_EXCHANGE_PARAMS)
+typedef struct {
+    ASN1_OBJECT *key_params;
+    ASN1_OBJECT *hash_params;
+    ASN1_OBJECT *cipher_params;
+} GOST_KEY_PARAMS;
+
+DECLARE_ASN1_FUNCTIONS(GOST_KEY_PARAMS)
+
+typedef struct {
+    ASN1_OCTET_STRING *iv;
+    ASN1_OBJECT *enc_param_set;
+} GOST_CIPHER_PARAMS;
+
+DECLARE_ASN1_FUNCTIONS(GOST_CIPHER_PARAMS)
+/*============== Message digest  and cipher related structures  ==========*/
+         /*
+          * Structure used as EVP_MD_CTX-md_data. It allows to avoid storing
+          * in the md-data pointers to dynamically allocated memory. I
+          * cannot invent better way to avoid memory leaks, because openssl
+          * insist on invoking Init on Final-ed digests, and there is no
+          * reliable way to find out whether pointer in the passed md_data is
+          * valid or not.
+          */
+struct ossl_gost_digest_ctx {
+    gost_hash_ctx dctx;
+    gost_ctx cctx;
+};
+/* EVP_MD structure for GOST R 34.11 */
+extern EVP_MD digest_gost;
+/* EVP_MD structure for GOST 28147 in MAC mode */
+extern EVP_MD imit_gost_cpa;
+/* Cipher context used for EVP_CIPHER operation */
+struct ossl_gost_cipher_ctx {
+    int paramNID;
+    unsigned int count;
+    int key_meshing;
+    gost_ctx cctx;
+};
+/* Structure to map parameter NID to S-block */
+struct gost_cipher_info {
+    int nid;
+    gost_subst_block *sblock;
+    int key_meshing;
+};
+/* Context for MAC */
+struct ossl_gost_imit_ctx {
+    gost_ctx cctx;
+    unsigned char buffer[8];
+    unsigned char partial_block[8];
+    unsigned int count;
+    int key_meshing;
+    int bytes_left;
+    int key_set;
+};
+/* Table which maps parameter NID to S-blocks */
+extern struct gost_cipher_info gost_cipher_list[];
+/* Find encryption params from ASN1_OBJECT */
+const struct gost_cipher_info *get_encryption_params(ASN1_OBJECT *obj);
+/* Implementation of GOST 28147-89 cipher in CFB and CNT modes */
+extern EVP_CIPHER cipher_gost;
+extern EVP_CIPHER cipher_gost_cpacnt;
+# define EVP_MD_CTRL_KEY_LEN (EVP_MD_CTRL_ALG_CTRL+3)
+# define EVP_MD_CTRL_SET_KEY (EVP_MD_CTRL_ALG_CTRL+4)
+/* EVP_PKEY_METHOD key encryption callbacks */
+/* From gost94_keyx.c */
+int pkey_GOST94cp_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out,
+                          size_t *outlen, const unsigned char *key,
+                          size_t key_len);
+
+int pkey_GOST94cp_decrypt(EVP_PKEY_CTX *ctx, unsigned char *out,
+                          size_t *outlen, const unsigned char *in,
+                          size_t in_len);
+/* From gost2001_keyx.c */
+int pkey_GOST01cp_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out,
+                          size_t *outlen, const unsigned char *key,
+                          size_t key_len);
+
+int pkey_GOST01cp_decrypt(EVP_PKEY_CTX *ctx, unsigned char *out,
+                          size_t *outlen, const unsigned char *in,
+                          size_t in_len);
+/* derive functions */
+/* From gost2001_keyx.c */
+int pkey_gost2001_derive(EVP_PKEY_CTX *ctx, unsigned char *key,
+                         size_t *keylen);
+/* From gost94_keyx.c */
+int pkey_gost94_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen);
+/* Internal functions for signature algorithms */
+int fill_GOST94_params(DSA *dsa, int nid);
+int fill_GOST2001_params(EC_KEY *eckey, int nid);
+int gost_sign_keygen(DSA *dsa);
+int gost2001_keygen(EC_KEY *ec);
+
+DSA_SIG *gost_do_sign(const unsigned char *dgst, int dlen, DSA *dsa);
+DSA_SIG *gost2001_do_sign(const unsigned char *dgst, int dlen, EC_KEY *eckey);
+
+int gost_do_verify(const unsigned char *dgst, int dgst_len,
+                   DSA_SIG *sig, DSA *dsa);
+int gost2001_do_verify(const unsigned char *dgst, int dgst_len,
+                       DSA_SIG *sig, EC_KEY *ec);
+int gost2001_compute_public(EC_KEY *ec);
+int gost94_compute_public(DSA *dsa);
+/*============== miscellaneous functions============================= */
+/* from gost_sign.c */
+/* Convert GOST R 34.11 hash sum to bignum according to standard */
+BIGNUM *hashsum2bn(const unsigned char *dgst);
+/*
+ * Store bignum in byte array of given length, prepending by zeros if
+ * nesseccary
+ */
+int store_bignum(BIGNUM *bn, unsigned char *buf, int len);
+/* Read bignum, which can have few MSB all-zeros    from buffer*/
+BIGNUM *getbnfrombuf(const unsigned char *buf, size_t len);
+/* Pack GOST R 34.10 signature according to CryptoPro rules */
+int pack_sign_cp(DSA_SIG *s, int order, unsigned char *sig, size_t *siglen);
+/* Unpack GOST R 34.10 signature according to CryptoPro rules */
+DSA_SIG *unpack_cp_signature(const unsigned char *sig, size_t siglen);
+/* from ameth.c */
+/* Get private key as BIGNUM from both R 34.10-94 and R 34.10-2001  keys*/
+/* Returns pointer into EVP_PKEY structure */
+BIGNUM *gost_get0_priv_key(const EVP_PKEY *pkey);
+/* Find NID by GOST 94 parameters */
+int gost94_nid_by_params(DSA *p);
+
+#endif
diff --git a/gost_md.c b/gost_md.c
new file mode 100644 (file)
index 0000000..1ccc6be
--- /dev/null
+++ b/gost_md.c
@@ -0,0 +1,76 @@
+/**********************************************************************
+ *                          md_gost.c                                 *
+ *             Copyright (c) 2005-2006 Cryptocom LTD                  *
+ *         This file is distributed under the same license as OpenSSL *
+ *                                                                    *
+ *       OpenSSL interface to GOST R 34.11-94 hash functions          *
+ *          Requires OpenSSL 0.9.9 for compilation                    *
+ **********************************************************************/
+#include <string.h>
+#include "gost_lcl.h"
+#include "gosthash.h"
+#include "e_gost_err.h"
+
+/* implementation of GOST 34.11 hash function See gost_md.c*/
+static int gost_digest_init(EVP_MD_CTX *ctx);
+static int gost_digest_update(EVP_MD_CTX *ctx, const void *data,
+                              size_t count);
+static int gost_digest_final(EVP_MD_CTX *ctx, unsigned char *md);
+static int gost_digest_copy(EVP_MD_CTX *to, const EVP_MD_CTX *from);
+static int gost_digest_cleanup(EVP_MD_CTX *ctx);
+
+EVP_MD digest_gost = {
+    NID_id_GostR3411_94,
+    NID_undef,
+    32,
+    EVP_MD_FLAG_PKEY_METHOD_SIGNATURE,
+    gost_digest_init,
+    gost_digest_update,
+    gost_digest_final,
+    gost_digest_copy,
+    gost_digest_cleanup,
+    NULL,
+    NULL,
+    {NID_undef, NID_undef, 0, 0, 0},
+    32,
+    sizeof(struct ossl_gost_digest_ctx),
+    NULL
+};
+
+int gost_digest_init(EVP_MD_CTX *ctx)
+{
+    struct ossl_gost_digest_ctx *c = ctx->md_data;
+    memset(&(c->dctx), 0, sizeof(gost_hash_ctx));
+    gost_init(&(c->cctx), &GostR3411_94_CryptoProParamSet);
+    c->dctx.cipher_ctx = &(c->cctx);
+    return 1;
+}
+
+int gost_digest_update(EVP_MD_CTX *ctx, const void *data, size_t count)
+{
+    return hash_block((gost_hash_ctx *) ctx->md_data, data, count);
+}
+
+int gost_digest_final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+    return finish_hash((gost_hash_ctx *) ctx->md_data, md);
+
+}
+
+int gost_digest_copy(EVP_MD_CTX *to, const EVP_MD_CTX *from)
+{
+    struct ossl_gost_digest_ctx *md_ctx = to->md_data;
+    if (to->md_data && from->md_data) {
+        memcpy(to->md_data, from->md_data,
+               sizeof(struct ossl_gost_digest_ctx));
+        md_ctx->dctx.cipher_ctx = &(md_ctx->cctx);
+    }
+    return 1;
+}
+
+int gost_digest_cleanup(EVP_MD_CTX *ctx)
+{
+    if (ctx->md_data)
+        memset(ctx->md_data, 0, sizeof(struct ossl_gost_digest_ctx));
+    return 1;
+}
diff --git a/gost_params.c b/gost_params.c
new file mode 100644 (file)
index 0000000..0411534
--- /dev/null
@@ -0,0 +1,207 @@
+/**********************************************************************
+ *                        params.c                                    *
+ *             Copyright (c) 2005-2006 Cryptocom LTD                  *
+ *         This file is distributed under the same license as OpenSSL *
+ *                                                                    *
+ * Definitions of GOST R 34.10 parameter sets, defined in RFC 4357    *
+ *         OpenSSL 0.9.9 libraries required to compile and use        *
+ *                              this code                             *
+ **********************************************************************/
+#include "gost_params.h"
+#include <openssl/objects.h>
+/* Parameters of GOST 34.10 */
+
+R3410_params R3410_paramset[] = {
+/* Paramset A */
+    {NID_id_GostR3410_94_CryptoPro_A_ParamSet,
+     "100997906755055304772081815535925224869"
+     "8410825720534578748235158755771479905292727772441528526992987964833"
+     "5669968284202797289605274717317548059048560713474685214192868091256"
+     "1502802222185647539190902656116367847270145019066794290930185446216"
+     "3997308722217328898303231940973554032134009725883228768509467406639"
+     "62",
+     "127021248288932417465907042777176443525"
+     "7876535089165358128175072657050312609850984974231883334834011809259"
+     "9999512098893413065920561499672425412104927434935707492031276956145"
+     "1689224110579311248812610229678534638401693520013288995000362260684"
+     "2227508135323070045173416336850045410625869714168836867788425378203"
+     "83",
+     "683631961449557007844441656118272528951"
+     "02170888761442055095051287550314083023"}
+    ,
+    {NID_id_GostR3410_94_CryptoPro_B_ParamSet,
+     "429418261486158041438734477379555023926"
+     "7234596860714306679811299408947123142002706038521669956384871995765"
+     "7284814898909770759462613437669456364882730370838934791080835932647"
+     "9767786019153434744009610342313166725786869204821949328786333602033"
+     "8479709268434224762105576023501613261478065276102850944540333865234"
+     "1",
+     "139454871199115825601409655107690713107"
+     "0417070599280317977580014543757653577229840941243685222882398330391"
+     "1468164807668823692122073732267216074074777170091113455043205380464"
+     "7694904686120113087816240740184800477047157336662926249423571248823"
+     "9685422217536601433914856808405203368594584948031873412885804895251"
+     "63",
+     "79885141663410976897627118935756323747307951916507639758300472692338873533959"}
+    ,
+    {NID_id_GostR3410_94_CryptoPro_C_ParamSet,
+     "816552717970881016017893191415300348226"
+     "2544051353358162468249467681876621283478212884286545844013955142622"
+     "2087723485023722868022275009502224827866201744494021697716482008353"
+     "6398202298024892620480898699335508064332313529725332208819456895108"
+     "5155178100221003459370588291073071186553005962149936840737128710832"
+     "3",
+     "110624679233511963040518952417017040248"
+     "5862954819831383774196396298584395948970608956170224210628525560327"
+     "8638246716655439297654402921844747893079518669992827880792192992701"
+     "1428546551433875806377110443534293554066712653034996277099320715774"
+     "3542287621283671843703709141350171945045805050291770503634517804938"
+     "01",
+     "113468861199819350564868233378875198043"
+     "267947776488510997961231672532899549103"}
+    ,
+    {NID_id_GostR3410_94_CryptoPro_D_ParamSet,
+     "756976611021707301782128757801610628085"
+     "5283803109571158829574281419208532589041660017017859858216341400371"
+     "4687551412794400562878935266630754392677014598582103365983119173924"
+     "4732511225464712252386803315902707727668715343476086350472025298282"
+     "7271461690125050616858238384366331089777463541013033926723743254833"
+     "7",
+     "905457649621929965904290958774625315611"
+     "3056083907389766971404812524422262512556054474620855996091570786713"
+     "5849550236741915584185990627801066465809510095784713989819413820871"
+     "5964648914493053407920737078890520482730623038837767710173664838239"
+     "8574828787891286471201460474326612697849693665518073864436497893214"
+     "9",
+     "108988435796353506912374591498972192620"
+     "190487557619582334771735390599299211593"}
+    ,
+
+    {NID_id_GostR3410_94_CryptoPro_XchA_ParamSet,
+     "1335318132727206734338595199483190012179423759678474868994823595993"
+     "6964252873471246159040332773182141032801252925387191478859899310331"
+     "0567744136196364803064721377826656898686468463277710150809401182608"
+     "7702016153249904683329312949209127762411378780302243557466062839716"
+     "59376426832674269780880061631528163475887",
+     "14201174159756348119636828602231808974327613839524373876287257344192"
+     "74593935127189736311660784676003608489466235676257952827747192122419"
+     "29071046134208380636394084512691828894000571524625445295769349356752"
+     "72895683154177544176313938445719175509684710784659566254794231229333"
+     "8483924514339614727760681880609734239",
+     "91771529896554605945588149018382750217296858393520724172743325725474"
+     "374979801"}
+    ,
+    {NID_id_GostR3410_94_CryptoPro_XchB_ParamSet,
+     "8890864727828423151699995801875757891031463338652579140051973659"
+     "3048131440685857067369829407947744496306656291505503608252399443"
+     "7900272386749145996230867832228661977543992816745254823298629859"
+     "8753575466286051738837854736167685769017780335804511440773337196"
+     "2538423532919394477873664752824509986617878992443177",
+     "1028946126624994859676552074360530315217970499989304888248413244"
+     "8474923022758470167998871003604670704877377286176171227694098633"
+     "1539089568784129110109512690503345393869871295783467257264868341"
+     "7200196629860561193666752429682367397084815179752036423595736533"
+     "68957392061769855284593965042530895046088067160269433",
+     "9109671391802626916582318050603555673628769498182593088388796888"
+     "5281641595199"}
+    ,
+    {NID_id_GostR3410_94_CryptoPro_XchC_ParamSet,
+     "4430618464297584182473135030809859326863990650118941756995270074"
+     "8609973181426950235239623239110557450826919295792878938752101867"
+     "7047181623251027516953100431855964837602657827828194249605561893"
+     "6965865325513137194483136247773653468410118796740709840825496997"
+     "9375560722345106704721086025979309968763193072908334",
+     "1246996366993477513607147265794064436203408861395055989217248455"
+     "7299870737698999651480662364723992859320868822848751165438350943"
+     "3276647222625940615560580450040947211826027729977563540237169063"
+     "0448079715771649447778447000597419032457722226253269698374446528"
+     "35352729304393746106576383349151001715930924115499549",
+     "6787876137336591234380295020065682527118129468050147943114675429"
+     "4748422492761"}
+    ,
+
+    {NID_undef, NULL, NULL, NULL}
+};
+
+R3410_2001_params R3410_2001_paramset[] = {
+    /* default_cc_sign01_param 1.2.643.2.9.1.8.1 */
+    {NID_id_GostR3410_2001_ParamSet_cc,
+     /* A */
+     "C0000000000000000000000000000000000000000000000000000000000003c4",
+     /* B */
+     "2d06B4265ebc749ff7d0f1f1f88232e81632e9088fd44b7787d5e407e955080c",
+     /* P */
+     "C0000000000000000000000000000000000000000000000000000000000003C7",
+     /* Q */
+     "5fffffffffffffffffffffffffffffff606117a2f4bde428b7458a54b6e87b85",
+     /* X */
+     "2",
+     /* Y */
+     "a20e034bf8813ef5c18d01105e726a17eb248b264ae9706f440bedc8ccb6b22c"}
+    ,
+    /* 1.2.643.2.2.35.0 */
+    {NID_id_GostR3410_2001_TestParamSet,
+     "7",
+     "5FBFF498AA938CE739B8E022FBAFEF40563F6E6A3472FC2A514C0CE9DAE23B7E",
+     "8000000000000000000000000000000000000000000000000000000000000431",
+     "8000000000000000000000000000000150FE8A1892976154C59CFC193ACCF5B3",
+     "2",
+     "08E2A8A0E65147D4BD6316030E16D19C85C97F0A9CA267122B96ABBCEA7E8FC8"}
+    ,
+    /*
+     * 1.2.643.2.2.35.1
+     */
+    {NID_id_GostR3410_2001_CryptoPro_A_ParamSet,
+     "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94",
+     "a6",
+     "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97",
+     "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C611070995AD10045841B09B761B893",
+     "1",
+     "8D91E471E0989CDA27DF505A453F2B7635294F2DDF23E3B122ACC99C9E9F1E14"}
+    ,
+    /*
+     * 1.2.643.2.2.35.2
+     */
+    {NID_id_GostR3410_2001_CryptoPro_B_ParamSet,
+     "8000000000000000000000000000000000000000000000000000000000000C96",
+     "3E1AF419A269A5F866A7D3C25C3DF80AE979259373FF2B182F49D4CE7E1BBC8B",
+     "8000000000000000000000000000000000000000000000000000000000000C99",
+     "800000000000000000000000000000015F700CFFF1A624E5E497161BCC8A198F",
+     "1",
+     "3FA8124359F96680B83D1C3EB2C070E5C545C9858D03ECFB744BF8D717717EFC"}
+    ,
+    /*
+     * 1.2.643.2.2.35.3
+     */
+    {NID_id_GostR3410_2001_CryptoPro_C_ParamSet,
+     "9B9F605F5A858107AB1EC85E6B41C8AACF846E86789051D37998F7B9022D7598",
+     "805a",
+     "9B9F605F5A858107AB1EC85E6B41C8AACF846E86789051D37998F7B9022D759B",
+     "9B9F605F5A858107AB1EC85E6B41C8AA582CA3511EDDFB74F02F3A6598980BB9",
+     "0",
+     "41ECE55743711A8C3CBF3783CD08C0EE4D4DC440D4641A8F366E550DFDB3BB67"}
+    ,
+    /*
+     * 1.2.643.2.2.36.0
+     */
+    {NID_id_GostR3410_2001_CryptoPro_XchA_ParamSet,
+     "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94",
+     "a6",
+     "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97",
+     "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C611070995AD10045841B09B761B893",
+     "1",
+     "8D91E471E0989CDA27DF505A453F2B7635294F2DDF23E3B122ACC99C9E9F1E14"}
+    ,
+    /*
+     * 1.2.643.2.2.36.1
+     */
+    {NID_id_GostR3410_2001_CryptoPro_XchB_ParamSet,
+     "9B9F605F5A858107AB1EC85E6B41C8AACF846E86789051D37998F7B9022D7598",
+     "805a",
+     "9B9F605F5A858107AB1EC85E6B41C8AACF846E86789051D37998F7B9022D759B",
+     "9B9F605F5A858107AB1EC85E6B41C8AA582CA3511EDDFB74F02F3A6598980BB9",
+     "0",
+     "41ECE55743711A8C3CBF3783CD08C0EE4D4DC440D4641A8F366E550DFDB3BB67"}
+    ,
+    {0, NULL, NULL, NULL, NULL, NULL, NULL}
+};
diff --git a/gost_params.h b/gost_params.h
new file mode 100644 (file)
index 0000000..0773cbf
--- /dev/null
@@ -0,0 +1,34 @@
+/**********************************************************************
+ *                        gost_params.h                               *
+ *             Copyright (c) 2005-2006 Cryptocom LTD                  *
+ *       This file is distributed under the same license as OpenSSL   *
+ *                                                                    *
+ *       Declaration of structures used to represent  GOST R 34.10    *
+ *                     parameter sets, defined in RFC 4357                *
+ *         OpenSSL 0.9.9 libraries required to compile and use        *
+ *                              this code                             *
+ **********************************************************************/
+#ifndef GOST_PARAMSET_H
+# define GOST_PARAMSET_H
+typedef struct R3410 {
+    int nid;
+    char *a;
+    char *p;
+    char *q;
+} R3410_params;
+
+extern R3410_params R3410_paramset[];
+
+typedef struct R3410_2001 {
+    int nid;
+    char *a;
+    char *b;
+    char *p;
+    char *q;
+    char *x;
+    char *y;
+} R3410_2001_params;
+
+extern R3410_2001_params R3410_2001_paramset[];
+
+#endif
diff --git a/gost_pmeth.c b/gost_pmeth.c
new file mode 100644 (file)
index 0000000..4a79a85
--- /dev/null
@@ -0,0 +1,621 @@
+/**********************************************************************
+ *                          gost_pmeth.c                              *
+ *             Copyright (c) 2005-2006 Cryptocom LTD                  *
+ *         This file is distributed under the same license as OpenSSL *
+ *                                                                    *
+ *   Implementation of RFC 4357 (GOST R 34.10) Publick key method     *
+ *       for OpenSSL                                                  *
+ *          Requires OpenSSL 0.9.9 for compilation                    *
+ **********************************************************************/
+#include <openssl/evp.h>
+#include <openssl/objects.h>
+#include <openssl/ec.h>
+#include <openssl/x509v3.h>     /* For string_to_hex */
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "gost_params.h"
+#include "gost_lcl.h"
+#include "e_gost_err.h"
+/* -----init, cleanup, copy - uniform for all algs  ---------------*/
+/* Allocates new gost_pmeth_data structure and assigns it as data */
+static int pkey_gost_init(EVP_PKEY_CTX *ctx)
+{
+    struct gost_pmeth_data *data;
+    EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(ctx);
+    data = OPENSSL_malloc(sizeof(struct gost_pmeth_data));
+    if (!data)
+        return 0;
+    memset(data, 0, sizeof(struct gost_pmeth_data));
+    if (pkey && EVP_PKEY_get0(pkey)) {
+        switch (EVP_PKEY_base_id(pkey)) {
+        case NID_id_GostR3410_94:
+            data->sign_param_nid = gost94_nid_by_params(EVP_PKEY_get0(pkey));
+            break;
+        case NID_id_GostR3410_2001:
+            data->sign_param_nid =
+                EC_GROUP_get_curve_name(EC_KEY_get0_group
+                                        (EVP_PKEY_get0((EVP_PKEY *)pkey)));
+            break;
+        default:
+            return 0;
+        }
+    }
+    EVP_PKEY_CTX_set_data(ctx, data);
+    return 1;
+}
+
+/* Copies contents of gost_pmeth_data structure */
+static int pkey_gost_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src)
+{
+    struct gost_pmeth_data *dst_data, *src_data;
+    if (!pkey_gost_init(dst)) {
+        return 0;
+    }
+    src_data = EVP_PKEY_CTX_get_data(src);
+    dst_data = EVP_PKEY_CTX_get_data(dst);
+    *dst_data = *src_data;
+    if (src_data->shared_ukm) {
+        dst_data->shared_ukm = NULL;
+    }
+    return 1;
+}
+
+/* Frees up gost_pmeth_data structure */
+static void pkey_gost_cleanup(EVP_PKEY_CTX *ctx)
+{
+    struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(ctx);
+    if (data->shared_ukm)
+        OPENSSL_free(data->shared_ukm);
+    OPENSSL_free(data);
+}
+
+/* --------------------- control functions  ------------------------------*/
+static int pkey_gost_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
+{
+    struct gost_pmeth_data *pctx =
+        (struct gost_pmeth_data *)EVP_PKEY_CTX_get_data(ctx);
+    switch (type) {
+    case EVP_PKEY_CTRL_MD:
+        {
+            if (EVP_MD_type((const EVP_MD *)p2) != NID_id_GostR3411_94) {
+                GOSTerr(GOST_F_PKEY_GOST_CTRL, GOST_R_INVALID_DIGEST_TYPE);
+                return 0;
+            }
+            pctx->md = (EVP_MD *)p2;
+            return 1;
+        }
+        break;
+
+    case EVP_PKEY_CTRL_GET_MD:
+        *(const EVP_MD **)p2 = pctx->md;
+        return 1;
+
+    case EVP_PKEY_CTRL_PKCS7_ENCRYPT:
+    case EVP_PKEY_CTRL_PKCS7_DECRYPT:
+    case EVP_PKEY_CTRL_PKCS7_SIGN:
+    case EVP_PKEY_CTRL_DIGESTINIT:
+#ifndef OPENSSL_NO_CMS
+    case EVP_PKEY_CTRL_CMS_ENCRYPT:
+    case EVP_PKEY_CTRL_CMS_DECRYPT:
+    case EVP_PKEY_CTRL_CMS_SIGN:
+#endif
+        return 1;
+
+    case EVP_PKEY_CTRL_GOST_PARAMSET:
+        pctx->sign_param_nid = (int)p1;
+        return 1;
+    case EVP_PKEY_CTRL_SET_IV:
+        pctx->shared_ukm = OPENSSL_malloc((int)p1);
+        memcpy(pctx->shared_ukm, p2, (int)p1);
+        return 1;
+    case EVP_PKEY_CTRL_PEER_KEY:
+        if (p1 == 0 || p1 == 1) /* call from EVP_PKEY_derive_set_peer */
+            return 1;
+        if (p1 == 2)            /* TLS: peer key used? */
+            return pctx->peer_key_used;
+        if (p1 == 3)            /* TLS: peer key used! */
+            return (pctx->peer_key_used = 1);
+        return -2;
+    }
+    return -2;
+}
+
+static int pkey_gost_ctrl94_str(EVP_PKEY_CTX *ctx,
+                                const char *type, const char *value)
+{
+    int param_nid = 0;
+    if (!strcmp(type, param_ctrl_string)) {
+        if (!value) {
+            return 0;
+        }
+        if (strlen(value) == 1) {
+            switch (toupper((unsigned char)value[0])) {
+            case 'A':
+                param_nid = NID_id_GostR3410_94_CryptoPro_A_ParamSet;
+                break;
+            case 'B':
+                param_nid = NID_id_GostR3410_94_CryptoPro_B_ParamSet;
+                break;
+            case 'C':
+                param_nid = NID_id_GostR3410_94_CryptoPro_C_ParamSet;
+                break;
+            case 'D':
+                param_nid = NID_id_GostR3410_94_CryptoPro_D_ParamSet;
+                break;
+            default:
+                return 0;
+                break;
+            }
+        } else if ((strlen(value) == 2)
+                   && (toupper((unsigned char)value[0]) == 'X')) {
+            switch (toupper((unsigned char)value[1])) {
+            case 'A':
+                param_nid = NID_id_GostR3410_94_CryptoPro_XchA_ParamSet;
+                break;
+            case 'B':
+                param_nid = NID_id_GostR3410_94_CryptoPro_XchB_ParamSet;
+                break;
+            case 'C':
+                param_nid = NID_id_GostR3410_94_CryptoPro_XchC_ParamSet;
+                break;
+            default:
+                return 0;
+                break;
+            }
+        } else {
+            R3410_params *p = R3410_paramset;
+            param_nid = OBJ_txt2nid(value);
+            if (param_nid == NID_undef) {
+                return 0;
+            }
+            for (; p->nid != NID_undef; p++) {
+                if (p->nid == param_nid)
+                    break;
+            }
+            if (p->nid == NID_undef) {
+                GOSTerr(GOST_F_PKEY_GOST_CTRL94_STR, GOST_R_INVALID_PARAMSET);
+                return 0;
+            }
+        }
+
+        return pkey_gost_ctrl(ctx, EVP_PKEY_CTRL_GOST_PARAMSET,
+                              param_nid, NULL);
+    }
+    return -2;
+}
+
+static int pkey_gost_ctrl01_str(EVP_PKEY_CTX *ctx,
+                                const char *type, const char *value)
+{
+    int param_nid = 0;
+    if (!strcmp(type, param_ctrl_string)) {
+        if (!value) {
+            return 0;
+        }
+        if (strlen(value) == 1) {
+            switch (toupper((unsigned char)value[0])) {
+            case 'A':
+                param_nid = NID_id_GostR3410_2001_CryptoPro_A_ParamSet;
+                break;
+            case 'B':
+                param_nid = NID_id_GostR3410_2001_CryptoPro_B_ParamSet;
+                break;
+            case 'C':
+                param_nid = NID_id_GostR3410_2001_CryptoPro_C_ParamSet;
+                break;
+            case '0':
+                param_nid = NID_id_GostR3410_2001_TestParamSet;
+                break;
+            default:
+                return 0;
+                break;
+            }
+        } else if ((strlen(value) == 2)
+                   && (toupper((unsigned char)value[0]) == 'X')) {
+            switch (toupper((unsigned char)value[1])) {
+            case 'A':
+                param_nid = NID_id_GostR3410_2001_CryptoPro_XchA_ParamSet;
+                break;
+            case 'B':
+                param_nid = NID_id_GostR3410_2001_CryptoPro_XchB_ParamSet;
+                break;
+            default:
+                return 0;
+                break;
+            }
+        } else {
+            R3410_2001_params *p = R3410_2001_paramset;
+            param_nid = OBJ_txt2nid(value);
+            if (param_nid == NID_undef) {
+                return 0;
+            }
+            for (; p->nid != NID_undef; p++) {
+                if (p->nid == param_nid)
+                    break;
+            }
+            if (p->nid == NID_undef) {
+                GOSTerr(GOST_F_PKEY_GOST_CTRL01_STR, GOST_R_INVALID_PARAMSET);
+                return 0;
+            }
+        }
+
+        return pkey_gost_ctrl(ctx, EVP_PKEY_CTRL_GOST_PARAMSET,
+                              param_nid, NULL);
+    }
+    return -2;
+}
+
+/* --------------------- key generation  --------------------------------*/
+
+static int pkey_gost_paramgen_init(EVP_PKEY_CTX *ctx)
+{
+    return 1;
+}
+
+static int pkey_gost94_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
+{
+    struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(ctx);
+    DSA *dsa = NULL;
+    if (data->sign_param_nid == NID_undef) {
+        GOSTerr(GOST_F_PKEY_GOST94_PARAMGEN, GOST_R_NO_PARAMETERS_SET);
+        return 0;
+    }
+    dsa = DSA_new();
+    if (!fill_GOST94_params(dsa, data->sign_param_nid)) {
+        DSA_free(dsa);
+        return 0;
+    }
+    EVP_PKEY_assign(pkey, NID_id_GostR3410_94, dsa);
+    return 1;
+}
+
+static int pkey_gost01_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
+{
+    struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(ctx);
+    EC_KEY *ec = NULL;
+
+    if (data->sign_param_nid == NID_undef) {
+        GOSTerr(GOST_F_PKEY_GOST01_PARAMGEN, GOST_R_NO_PARAMETERS_SET);
+        return 0;
+    }
+    if (!ec)
+        ec = EC_KEY_new();
+    if (!fill_GOST2001_params(ec, data->sign_param_nid)) {
+        EC_KEY_free(ec);
+        return 0;
+    }
+    EVP_PKEY_assign(pkey, NID_id_GostR3410_2001, ec);
+    return 1;
+}
+
+/* Generates Gost_R3410_94_cp key */
+static int pkey_gost94cp_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
+{
+    DSA *dsa;
+    if (!pkey_gost94_paramgen(ctx, pkey))
+        return 0;
+    dsa = EVP_PKEY_get0(pkey);
+    gost_sign_keygen(dsa);
+    return 1;
+}
+
+/* Generates GOST_R3410 2001 key and assigns it using specified type */
+static int pkey_gost01cp_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
+{
+    EC_KEY *ec;
+    if (!pkey_gost01_paramgen(ctx, pkey))
+        return 0;
+    ec = EVP_PKEY_get0(pkey);
+    gost2001_keygen(ec);
+    return 1;
+}
+
+/* ----------- sign callbacks --------------------------------------*/
+
+static int pkey_gost94_cp_sign(EVP_PKEY_CTX *ctx, unsigned char *sig,
+                               size_t *siglen, const unsigned char *tbs,
+                               size_t tbs_len)
+{
+    DSA_SIG *unpacked_sig = NULL;
+    EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(ctx);
+    if (!siglen)
+        return 0;
+    if (!sig) {
+        *siglen = 64;           /* better to check size of pkey->pkey.dsa-q */
+        return 1;
+    }
+    unpacked_sig = gost_do_sign(tbs, tbs_len, EVP_PKEY_get0(pkey));
+    if (!unpacked_sig) {
+        return 0;
+    }
+    return pack_sign_cp(unpacked_sig, 32, sig, siglen);
+}
+
+static int pkey_gost01_cp_sign(EVP_PKEY_CTX *ctx, unsigned char *sig,
+                               size_t *siglen, const unsigned char *tbs,
+                               size_t tbs_len)
+{
+    DSA_SIG *unpacked_sig = NULL;
+    EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(ctx);
+    if (!siglen)
+        return 0;
+    if (!sig) {
+        *siglen = 64;           /* better to check size of curve order */
+        return 1;
+    }
+    unpacked_sig = gost2001_do_sign(tbs, tbs_len, EVP_PKEY_get0(pkey));
+    if (!unpacked_sig) {
+        return 0;
+    }
+    return pack_sign_cp(unpacked_sig, 32, sig, siglen);
+}
+
+/* ------------------- verify callbacks ---------------------------*/
+
+static int pkey_gost94_cp_verify(EVP_PKEY_CTX *ctx, const unsigned char *sig,
+                                 size_t siglen, const unsigned char *tbs,
+                                 size_t tbs_len)
+{
+    int ok = 0;
+    EVP_PKEY *pub_key = EVP_PKEY_CTX_get0_pkey(ctx);
+    DSA_SIG *s = unpack_cp_signature(sig, siglen);
+    if (!s)
+        return 0;
+    if (pub_key)
+        ok = gost_do_verify(tbs, tbs_len, s, EVP_PKEY_get0(pub_key));
+    DSA_SIG_free(s);
+    return ok;
+}
+
+static int pkey_gost01_cp_verify(EVP_PKEY_CTX *ctx, const unsigned char *sig,
+                                 size_t siglen, const unsigned char *tbs,
+                                 size_t tbs_len)
+{
+    int ok = 0;
+    EVP_PKEY *pub_key = EVP_PKEY_CTX_get0_pkey(ctx);
+    DSA_SIG *s = unpack_cp_signature(sig, siglen);
+    if (!s)
+        return 0;
+#ifdef DEBUG_SIGN
+    fprintf(stderr, "R=");
+    BN_print_fp(stderr, s->r);
+    fprintf(stderr, "\nS=");
+    BN_print_fp(stderr, s->s);
+    fprintf(stderr, "\n");
+#endif
+    if (pub_key)
+        ok = gost2001_do_verify(tbs, tbs_len, s, EVP_PKEY_get0(pub_key));
+    DSA_SIG_free(s);
+    return ok;
+}
+
+/* ------------- encrypt init -------------------------------------*/
+/* Generates ephermeral key */
+static int pkey_gost_encrypt_init(EVP_PKEY_CTX *ctx)
+{
+    return 1;
+}
+
+/* --------------- Derive init ------------------------------------*/
+static int pkey_gost_derive_init(EVP_PKEY_CTX *ctx)
+{
+    return 1;
+}
+
+/* -------- PKEY_METHOD for GOST MAC algorithm --------------------*/
+static int pkey_gost_mac_init(EVP_PKEY_CTX *ctx)
+{
+    struct gost_mac_pmeth_data *data;
+    data = OPENSSL_malloc(sizeof(struct gost_mac_pmeth_data));
+    if (!data)
+        return 0;
+    memset(data, 0, sizeof(struct gost_mac_pmeth_data));
+    EVP_PKEY_CTX_set_data(ctx, data);
+    return 1;
+}
+
+static void pkey_gost_mac_cleanup(EVP_PKEY_CTX *ctx)
+{
+    struct gost_mac_pmeth_data *data = EVP_PKEY_CTX_get_data(ctx);
+    OPENSSL_free(data);
+}
+
+static int pkey_gost_mac_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src)
+{
+    struct gost_mac_pmeth_data *dst_data, *src_data;
+    if (!pkey_gost_mac_init(dst)) {
+        return 0;
+    }
+    src_data = EVP_PKEY_CTX_get_data(src);
+    dst_data = EVP_PKEY_CTX_get_data(dst);
+    *dst_data = *src_data;
+    return 1;
+}
+
+static int pkey_gost_mac_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
+{
+    struct gost_mac_pmeth_data *data =
+        (struct gost_mac_pmeth_data *)EVP_PKEY_CTX_get_data(ctx);
+
+    switch (type) {
+    case EVP_PKEY_CTRL_MD:
+        {
+            if (EVP_MD_type((const EVP_MD *)p2) != NID_id_Gost28147_89_MAC) {
+                GOSTerr(GOST_F_PKEY_GOST_MAC_CTRL,
+                        GOST_R_INVALID_DIGEST_TYPE);
+                return 0;
+            }
+            data->md = (EVP_MD *)p2;
+            return 1;
+        }
+        break;
+
+    case EVP_PKEY_CTRL_GET_MD:
+        *(const EVP_MD **)p2 = data->md;
+        return 1;
+
+    case EVP_PKEY_CTRL_PKCS7_ENCRYPT:
+    case EVP_PKEY_CTRL_PKCS7_DECRYPT:
+    case EVP_PKEY_CTRL_PKCS7_SIGN:
+        return 1;
+    case EVP_PKEY_CTRL_SET_MAC_KEY:
+        if (p1 != 32) {
+            GOSTerr(GOST_F_PKEY_GOST_MAC_CTRL, GOST_R_INVALID_MAC_KEY_LENGTH);
+            return 0;
+        }
+
+        memcpy(data->key, p2, 32);
+        data->key_set = 1;
+        return 1;
+    case EVP_PKEY_CTRL_DIGESTINIT:
+        {
+            EVP_MD_CTX *mctx = p2;
+            void *key;
+            if (!data->key_set) {
+                EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(ctx);
+                if (!pkey) {
+                    GOSTerr(GOST_F_PKEY_GOST_MAC_CTRL,
+                            GOST_R_MAC_KEY_NOT_SET);
+                    return 0;
+                }
+                key = EVP_PKEY_get0(pkey);
+                if (!key) {
+                    GOSTerr(GOST_F_PKEY_GOST_MAC_CTRL,
+                            GOST_R_MAC_KEY_NOT_SET);
+                    return 0;
+                }
+            } else {
+                key = &(data->key);
+            }
+            return mctx->digest->md_ctrl(mctx, EVP_MD_CTRL_SET_KEY, 32, key);
+        }
+    }
+    return -2;
+}
+
+static int pkey_gost_mac_ctrl_str(EVP_PKEY_CTX *ctx,
+                                  const char *type, const char *value)
+{
+    if (!strcmp(type, key_ctrl_string)) {
+        if (strlen(value) != 32) {
+            GOSTerr(GOST_F_PKEY_GOST_MAC_CTRL_STR,
+                    GOST_R_INVALID_MAC_KEY_LENGTH);
+            return 0;
+        }
+        return pkey_gost_mac_ctrl(ctx, EVP_PKEY_CTRL_SET_MAC_KEY,
+                                  32, (char *)value);
+    }
+    if (!strcmp(type, hexkey_ctrl_string)) {
+        long keylen;
+        int ret;
+        unsigned char *keybuf = string_to_hex(value, &keylen);
+        if (!keybuf || keylen != 32) {
+            GOSTerr(GOST_F_PKEY_GOST_MAC_CTRL_STR,
+                    GOST_R_INVALID_MAC_KEY_LENGTH);
+            OPENSSL_free(keybuf);
+            return 0;
+        }
+        ret = pkey_gost_mac_ctrl(ctx, EVP_PKEY_CTRL_SET_MAC_KEY, 32, keybuf);
+        OPENSSL_free(keybuf);
+        return ret;
+
+    }
+    return -2;
+}
+
+static int pkey_gost_mac_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
+{
+    struct gost_mac_pmeth_data *data = EVP_PKEY_CTX_get_data(ctx);
+    unsigned char *keydata;
+    if (!data->key_set) {
+        GOSTerr(GOST_F_PKEY_GOST_MAC_KEYGEN, GOST_R_MAC_KEY_NOT_SET);
+        return 0;
+    }
+    keydata = OPENSSL_malloc(32);
+    memcpy(keydata, data->key, 32);
+    EVP_PKEY_assign(pkey, NID_id_Gost28147_89_MAC, keydata);
+    return 1;
+}
+
+static int pkey_gost_mac_signctx_init(EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx)
+{
+    return 1;
+}
+
+static int pkey_gost_mac_signctx(EVP_PKEY_CTX *ctx, unsigned char *sig,
+                                 size_t *siglen, EVP_MD_CTX *mctx)
+{
+    unsigned int tmpsiglen = *siglen; /* for platforms where
+                                       * sizeof(int)!=sizeof(size_t) */
+    int ret;
+    if (!sig) {
+        *siglen = 4;
+        return 1;
+    }
+    ret = EVP_DigestFinal_ex(mctx, sig, &tmpsiglen);
+    *siglen = tmpsiglen;
+    return ret;
+}
+
+/* ----------------------------------------------------------------*/
+int register_pmeth_gost(int id, EVP_PKEY_METHOD **pmeth, int flags)
+{
+    *pmeth = EVP_PKEY_meth_new(id, flags);
+    if (!*pmeth)
+        return 0;
+
+    switch (id) {
+    case NID_id_GostR3410_94:
+        EVP_PKEY_meth_set_ctrl(*pmeth, pkey_gost_ctrl, pkey_gost_ctrl94_str);
+        EVP_PKEY_meth_set_keygen(*pmeth, NULL, pkey_gost94cp_keygen);
+        EVP_PKEY_meth_set_sign(*pmeth, NULL, pkey_gost94_cp_sign);
+        EVP_PKEY_meth_set_verify(*pmeth, NULL, pkey_gost94_cp_verify);
+        EVP_PKEY_meth_set_encrypt(*pmeth,
+                                  pkey_gost_encrypt_init,
+                                  pkey_GOST94cp_encrypt);
+        EVP_PKEY_meth_set_decrypt(*pmeth, NULL, pkey_GOST94cp_decrypt);
+        EVP_PKEY_meth_set_derive(*pmeth,
+                                 pkey_gost_derive_init, pkey_gost94_derive);
+        EVP_PKEY_meth_set_paramgen(*pmeth, pkey_gost_paramgen_init,
+                                   pkey_gost94_paramgen);
+        break;
+    case NID_id_GostR3410_2001:
+        EVP_PKEY_meth_set_ctrl(*pmeth, pkey_gost_ctrl, pkey_gost_ctrl01_str);
+        EVP_PKEY_meth_set_sign(*pmeth, NULL, pkey_gost01_cp_sign);
+        EVP_PKEY_meth_set_verify(*pmeth, NULL, pkey_gost01_cp_verify);
+
+        EVP_PKEY_meth_set_keygen(*pmeth, NULL, pkey_gost01cp_keygen);
+
+        EVP_PKEY_meth_set_encrypt(*pmeth,
+                                  pkey_gost_encrypt_init,
+                                  pkey_GOST01cp_encrypt);
+        EVP_PKEY_meth_set_decrypt(*pmeth, NULL, pkey_GOST01cp_decrypt);
+        EVP_PKEY_meth_set_derive(*pmeth,
+                                 pkey_gost_derive_init, pkey_gost2001_derive);
+        EVP_PKEY_meth_set_paramgen(*pmeth, pkey_gost_paramgen_init,
+                                   pkey_gost01_paramgen);
+        break;
+    case NID_id_Gost28147_89_MAC:
+        EVP_PKEY_meth_set_ctrl(*pmeth, pkey_gost_mac_ctrl,
+                               pkey_gost_mac_ctrl_str);
+        EVP_PKEY_meth_set_signctx(*pmeth, pkey_gost_mac_signctx_init,
+                                  pkey_gost_mac_signctx);
+        EVP_PKEY_meth_set_keygen(*pmeth, NULL, pkey_gost_mac_keygen);
+        EVP_PKEY_meth_set_init(*pmeth, pkey_gost_mac_init);
+        EVP_PKEY_meth_set_cleanup(*pmeth, pkey_gost_mac_cleanup);
+        EVP_PKEY_meth_set_copy(*pmeth, pkey_gost_mac_copy);
+        return 1;
+    default:                   /* Unsupported method */
+        return 0;
+    }
+    EVP_PKEY_meth_set_init(*pmeth, pkey_gost_init);
+    EVP_PKEY_meth_set_cleanup(*pmeth, pkey_gost_cleanup);
+
+    EVP_PKEY_meth_set_copy(*pmeth, pkey_gost_copy);
+    /*
+     * FIXME derive etc...
+     */
+
+    return 1;
+}
diff --git a/gost_sign.c b/gost_sign.c
new file mode 100644 (file)
index 0000000..07ad921
--- /dev/null
@@ -0,0 +1,373 @@
+/**********************************************************************
+ *                          gost_sign.c                               *
+ *             Copyright (c) 2005-2006 Cryptocom LTD                  *
+ *         This file is distributed under the same license as OpenSSL *
+ *                                                                    *
+ *       Implementation of GOST R 34.10-94 signature algorithm        *
+ *       for OpenSSL                                                  *
+ *          Requires OpenSSL 0.9.9 for compilation                    *
+ **********************************************************************/
+#include <string.h>
+#include <openssl/rand.h>
+#include <openssl/bn.h>
+#include <openssl/dsa.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+
+#include "gost_params.h"
+#include "gost_lcl.h"
+#include "e_gost_err.h"
+
+#ifdef DEBUG_SIGN
+void dump_signature(const char *message, const unsigned char *buffer,
+                    size_t len)
+{
+    size_t i;
+    fprintf(stderr, "signature %s Length=%d", message, len);
+    for (i = 0; i < len; i++) {
+        if (i % 16 == 0)
+            fputc('\n', stderr);
+        fprintf(stderr, " %02x", buffer[i]);
+    }
+    fprintf(stderr, "\nEnd of signature\n");
+}
+
+void dump_dsa_sig(const char *message, DSA_SIG *sig)
+{
+    fprintf(stderr, "%s\nR=", message);
+    BN_print_fp(stderr, sig->r);
+    fprintf(stderr, "\nS=");
+    BN_print_fp(stderr, sig->s);
+    fprintf(stderr, "\n");
+}
+
+#else
+
+# define dump_signature(a,b,c)
+# define dump_dsa_sig(a,b)
+#endif
+
+/*
+ * Computes signature and returns it as DSA_SIG structure
+ */
+DSA_SIG *gost_do_sign(const unsigned char *dgst, int dlen, DSA *dsa)
+{
+    BIGNUM *k = NULL, *tmp = NULL, *tmp2 = NULL;
+    DSA_SIG *newsig = NULL, *ret = NULL;
+    BIGNUM *md = hashsum2bn(dgst);
+    /* check if H(M) mod q is zero */
+    BN_CTX *ctx = BN_CTX_new();
+    if(!ctx) {
+        GOSTerr(GOST_F_GOST_DO_SIGN, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    BN_CTX_start(ctx);
+    newsig = DSA_SIG_new();
+    if (!newsig) {
+        GOSTerr(GOST_F_GOST_DO_SIGN, GOST_R_NO_MEMORY);
+        goto err;
+    }
+    tmp = BN_CTX_get(ctx);
+    k = BN_CTX_get(ctx);
+    tmp2 = BN_CTX_get(ctx);
+    if(!tmp || !k || !tmp2) {
+        GOSTerr(GOST_F_GOST_DO_SIGN, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    BN_mod(tmp, md, dsa->q, ctx);
+    if (BN_is_zero(tmp)) {
+        BN_one(md);
+    }
+    do {
+        do {
+            /*
+             * Generate random number k less than q
+             */
+            BN_rand_range(k, dsa->q);
+            /* generate r = (a^x mod p) mod q */
+            BN_mod_exp(tmp, dsa->g, k, dsa->p, ctx);
+            if (!(newsig->r)) {
+                newsig->r = BN_new();
+                if(!newsig->r) {
+                    GOSTerr(GOST_F_GOST_DO_SIGN, ERR_R_MALLOC_FAILURE);
+                    goto err;
+                }
+            }
+            BN_mod(newsig->r, tmp, dsa->q, ctx);
+        }
+        while (BN_is_zero(newsig->r));
+        /* generate s = (xr + k(Hm)) mod q */
+        BN_mod_mul(tmp, dsa->priv_key, newsig->r, dsa->q, ctx);
+        BN_mod_mul(tmp2, k, md, dsa->q, ctx);
+        if (!newsig->s) {
+            newsig->s = BN_new();
+            if(!newsig->s) {
+                GOSTerr(GOST_F_GOST_DO_SIGN, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+        }
+        BN_mod_add(newsig->s, tmp, tmp2, dsa->q, ctx);
+    }
+    while (BN_is_zero(newsig->s));
+
+    ret = newsig;
+ err:
+    BN_free(md);
+    if(ctx) {
+        BN_CTX_end(ctx);
+        BN_CTX_free(ctx);
+    }
+    if(!ret && newsig) {
+        DSA_SIG_free(newsig);
+    }
+    return ret;
+}
+
+/*
+ * Packs signature according to Cryptocom rules
+ * and frees up DSA_SIG structure
+ */
+/*-
+int pack_sign_cc(DSA_SIG *s,int order,unsigned char *sig, size_t *siglen)
+        {
+        *siglen = 2*order;
+        memset(sig,0,*siglen);
+        store_bignum(s->r, sig,order);
+        store_bignum(s->s, sig + order,order);
+        dump_signature("serialized",sig,*siglen);
+        DSA_SIG_free(s);
+        return 1;
+        }
+*/
+/*
+ * Packs signature according to Cryptopro rules
+ * and frees up DSA_SIG structure
+ */
+int pack_sign_cp(DSA_SIG *s, int order, unsigned char *sig, size_t *siglen)
+{
+    *siglen = 2 * order;
+    memset(sig, 0, *siglen);
+    store_bignum(s->s, sig, order);
+    store_bignum(s->r, sig + order, order);
+    dump_signature("serialized", sig, *siglen);
+    DSA_SIG_free(s);
+    return 1;
+}
+
+/*
+ * Verifies signature passed as DSA_SIG structure
+ *
+ */
+
+int gost_do_verify(const unsigned char *dgst, int dgst_len,
+                   DSA_SIG *sig, DSA *dsa)
+{
+    BIGNUM *md = NULL, *tmp = NULL;
+    BIGNUM *q2 = NULL;
+    BIGNUM *u = NULL, *v = NULL, *z1 = NULL, *z2 = NULL;
+    BIGNUM *tmp2 = NULL, *tmp3 = NULL;
+    int ok = 0;
+    BN_CTX *ctx = BN_CTX_new();
+    if(!ctx) {
+        GOSTerr(GOST_F_GOST_DO_VERIFY, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    BN_CTX_start(ctx);
+    if (BN_cmp(sig->s, dsa->q) >= 1 || BN_cmp(sig->r, dsa->q) >= 1) {
+        GOSTerr(GOST_F_GOST_DO_VERIFY, GOST_R_SIGNATURE_PARTS_GREATER_THAN_Q);
+        goto err;
+    }
+    md = hashsum2bn(dgst);
+
+    tmp = BN_CTX_get(ctx);
+    v = BN_CTX_get(ctx);
+    q2 = BN_CTX_get(ctx);
+    z1 = BN_CTX_get(ctx);
+    z2 = BN_CTX_get(ctx);
+    tmp2 = BN_CTX_get(ctx);
+    tmp3 = BN_CTX_get(ctx);
+    u = BN_CTX_get(ctx);
+    if(!tmp || !v || !q2 || !z1 || !z2 || !tmp2 || !tmp3 || !u) {
+        GOSTerr(GOST_F_GOST_DO_VERIFY, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    BN_mod(tmp, md, dsa->q, ctx);
+    if (BN_is_zero(tmp)) {
+        BN_one(md);
+    }
+    BN_copy(q2, dsa->q);
+    BN_sub_word(q2, 2);
+    BN_mod_exp(v, md, q2, dsa->q, ctx);
+    BN_mod_mul(z1, sig->s, v, dsa->q, ctx);
+    BN_sub(tmp, dsa->q, sig->r);
+    BN_mod_mul(z2, tmp, v, dsa->p, ctx);
+    BN_mod_exp(tmp, dsa->g, z1, dsa->p, ctx);
+    BN_mod_exp(tmp2, dsa->pub_key, z2, dsa->p, ctx);
+    BN_mod_mul(tmp3, tmp, tmp2, dsa->p, ctx);
+    BN_mod(u, tmp3, dsa->q, ctx);
+    ok = (BN_cmp(u, sig->r) == 0);
+
+    if (!ok) {
+        GOSTerr(GOST_F_GOST_DO_VERIFY, GOST_R_SIGNATURE_MISMATCH);
+    }
+err:
+    if(md) BN_free(md);
+    if(ctx) {
+        BN_CTX_end(ctx);
+        BN_CTX_free(ctx);
+    }
+    return ok;
+}
+
+/*
+ * Computes public keys for GOST R 34.10-94 algorithm
+ *
+ */
+int gost94_compute_public(DSA *dsa)
+{
+    /* Now fill algorithm parameters with correct values */
+    BN_CTX *ctx;
+    if (!dsa->g) {
+        GOSTerr(GOST_F_GOST94_COMPUTE_PUBLIC, GOST_R_KEY_IS_NOT_INITALIZED);
+        return 0;
+    }
+    ctx = BN_CTX_new();
+    if(!ctx) {
+        GOSTerr(GOST_F_GOST94_COMPUTE_PUBLIC, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    dsa->pub_key = BN_new();
+    if(!dsa->pub_key) {
+        GOSTerr(GOST_F_GOST94_COMPUTE_PUBLIC, ERR_R_MALLOC_FAILURE);
+        BN_CTX_free(ctx);
+        return 0;
+    }
+    /* Compute public key  y = a^x mod p */
+    BN_mod_exp(dsa->pub_key, dsa->g, dsa->priv_key, dsa->p, ctx);
+    BN_CTX_free(ctx);
+    return 1;
+}
+
+/*
+ * Fill GOST 94 params, searching them in R3410_paramset array
+ * by nid of paramset
+ *
+ */
+int fill_GOST94_params(DSA *dsa, int nid)
+{
+    R3410_params *params = R3410_paramset;
+    while (params->nid != NID_undef && params->nid != nid)
+        params++;
+    if (params->nid == NID_undef) {
+        GOSTerr(GOST_F_FILL_GOST94_PARAMS, GOST_R_UNSUPPORTED_PARAMETER_SET);
+        return 0;
+    }
+#define dump_signature(a,b,c)
+    if (dsa->p) {
+        BN_free(dsa->p);
+    }
+    dsa->p = NULL;
+    BN_dec2bn(&(dsa->p), params->p);
+    if (dsa->q) {
+        BN_free(dsa->q);
+    }
+    dsa->q = NULL;
+    BN_dec2bn(&(dsa->q), params->q);
+    if (dsa->g) {
+        BN_free(dsa->g);
+    }
+    dsa->g = NULL;
+    BN_dec2bn(&(dsa->g), params->a);
+    return 1;
+}
+
+/*
+ *  Generate GOST R 34.10-94 keypair
+ *
+ *
+ */
+int gost_sign_keygen(DSA *dsa)
+{
+    dsa->priv_key = BN_new();
+    if(!dsa->priv_key) {
+        GOSTerr(GOST_F_GOST_SIGN_KEYGEN, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    BN_rand_range(dsa->priv_key, dsa->q);
+    return gost94_compute_public(dsa);
+}
+
+/* Unpack signature according to cryptocom rules  */
+/*-
+DSA_SIG *unpack_cc_signature(const unsigned char *sig,size_t siglen)
+        {
+        DSA_SIG *s;
+        s = DSA_SIG_new();
+        if (s == NULL)
+                {
+                GOSTerr(GOST_F_UNPACK_CC_SIGNATURE,GOST_R_NO_MEMORY);
+                return(NULL);
+                }
+        s->r = getbnfrombuf(sig, siglen/2);
+        s->s = getbnfrombuf(sig + siglen/2, siglen/2);
+        return s;
+        }
+*/
+/* Unpack signature according to cryptopro rules  */
+DSA_SIG *unpack_cp_signature(const unsigned char *sig, size_t siglen)
+{
+    DSA_SIG *s;
+
+    s = DSA_SIG_new();
+    if (s == NULL) {
+        GOSTerr(GOST_F_UNPACK_CP_SIGNATURE, GOST_R_NO_MEMORY);
+        return NULL;
+    }
+    s->s = getbnfrombuf(sig, siglen / 2);
+    s->r = getbnfrombuf(sig + siglen / 2, siglen / 2);
+    return s;
+}
+
+/* Convert little-endian byte array into bignum */
+BIGNUM *hashsum2bn(const unsigned char *dgst)
+{
+    unsigned char buf[32];
+    int i;
+    for (i = 0; i < 32; i++) {
+        buf[31 - i] = dgst[i];
+    }
+    return getbnfrombuf(buf, 32);
+}
+
+/* Convert byte buffer to bignum, skipping leading zeros*/
+BIGNUM *getbnfrombuf(const unsigned char *buf, size_t len)
+{
+    while (*buf == 0 && len > 0) {
+        buf++;
+        len--;
+    }
+    if (len) {
+        return BN_bin2bn(buf, len, NULL);
+    } else {
+        BIGNUM *b = BN_new();
+        BN_zero(b);
+        return b;
+    }
+}
+
+/*
+ * Pack bignum into byte buffer of given size, filling all leading bytes by
+ * zeros
+ */
+int store_bignum(BIGNUM *bn, unsigned char *buf, int len)
+{
+    int bytes = BN_num_bytes(bn);
+    if (bytes > len)
+        return 0;
+    memset(buf, 0, len);
+    BN_bn2bin(bn, buf + len - bytes);
+    return 1;
+}
diff --git a/gosthash.c b/gosthash.c
new file mode 100644 (file)
index 0000000..72faa24
--- /dev/null
@@ -0,0 +1,268 @@
+/**********************************************************************
+ *                          gosthash.c                                *
+ *             Copyright (c) 2005-2006 Cryptocom LTD                  *
+ *         This file is distributed under the same license as OpenSSL *
+ *                                                                    *
+ *    Implementation of GOST R 34.11-94 hash function                 *
+ *       uses on gost89.c and gost89.h Doesn't need OpenSSL           *
+ **********************************************************************/
+#include <string.h>
+
+#include "gost89.h"
+#include "gosthash.h"
+
+/*
+ * Use OPENSSL_malloc for memory allocation if compiled with
+ * -DOPENSSL_BUILD, and libc malloc otherwise
+ */
+#ifndef MYALLOC
+# ifdef OPENSSL_BUILD
+#  include <openssl/crypto.h>
+#  define MYALLOC(size) OPENSSL_malloc(size)
+#  define MYFREE(ptr) OPENSSL_free(ptr)
+# else
+#  define MYALLOC(size) malloc(size)
+#  define MYFREE(ptr) free(ptr)
+# endif
+#endif
+/*
+ * Following functions are various bit meshing routines used in GOST R
+ * 34.11-94 algorithms
+ */
+static void swap_bytes(byte * w, byte * k)
+{
+    int i, j;
+    for (i = 0; i < 4; i++)
+        for (j = 0; j < 8; j++)
+            k[i + 4 * j] = w[8 * i + j];
+
+}
+
+/* was A_A */
+static void circle_xor8(const byte * w, byte * k)
+{
+    byte buf[8];
+    int i;
+    memcpy(buf, w, 8);
+    memmove(k, w + 8, 24);
+    for (i = 0; i < 8; i++)
+        k[i + 24] = buf[i] ^ k[i];
+}
+
+/* was R_R */
+static void transform_3(byte * data)
+{
+    unsigned short int acc;
+    acc = (data[0] ^ data[2] ^ data[4] ^ data[6] ^ data[24] ^ data[30]) |
+        ((data[1] ^ data[3] ^ data[5] ^ data[7] ^ data[25] ^ data[31]) << 8);
+    memmove(data, data + 2, 30);
+    data[30] = acc & 0xff;
+    data[31] = acc >> 8;
+}
+
+/* Adds blocks of N bytes modulo 2**(8*n). Returns carry*/
+static int add_blocks(int n, byte * left, const byte * right)
+{
+    int i;
+    int carry = 0;
+    int sum;
+    for (i = 0; i < n; i++) {
+        sum = (int)left[i] + (int)right[i] + carry;
+        left[i] = sum & 0xff;
+        carry = sum >> 8;
+    }
+    return carry;
+}
+
+/* Xor two sequences of bytes */
+static void xor_blocks(byte * result, const byte * a, const byte * b,
+                       size_t len)
+{
+    size_t i;
+    for (i = 0; i < len; i++)
+        result[i] = a[i] ^ b[i];
+}
+
+/*
+ *      Calculate H(i+1) = Hash(Hi,Mi)
+ *      Where H and M are 32 bytes long
+ */
+static int hash_step(gost_ctx * c, byte * H, const byte * M)
+{
+    byte U[32], W[32], V[32], S[32], Key[32];
+    int i;
+    /* Compute first key */
+    xor_blocks(W, H, M, 32);
+    swap_bytes(W, Key);
+    /* Encrypt first 8 bytes of H with first key */
+    gost_enc_with_key(c, Key, H, S);
+    /* Compute second key */
+    circle_xor8(H, U);
+    circle_xor8(M, V);
+    circle_xor8(V, V);
+    xor_blocks(W, U, V, 32);
+    swap_bytes(W, Key);
+    /* encrypt second 8 bytes of H with second key */
+    gost_enc_with_key(c, Key, H + 8, S + 8);
+    /* compute third key */
+    circle_xor8(U, U);
+    U[31] = ~U[31];
+    U[29] = ~U[29];
+    U[28] = ~U[28];
+    U[24] = ~U[24];
+    U[23] = ~U[23];
+    U[20] = ~U[20];
+    U[18] = ~U[18];
+    U[17] = ~U[17];
+    U[14] = ~U[14];
+    U[12] = ~U[12];
+    U[10] = ~U[10];
+    U[8] = ~U[8];
+    U[7] = ~U[7];
+    U[5] = ~U[5];
+    U[3] = ~U[3];
+    U[1] = ~U[1];
+    circle_xor8(V, V);
+    circle_xor8(V, V);
+    xor_blocks(W, U, V, 32);
+    swap_bytes(W, Key);
+    /* encrypt third 8 bytes of H with third key */
+    gost_enc_with_key(c, Key, H + 16, S + 16);
+    /* Compute fourth key */
+    circle_xor8(U, U);
+    circle_xor8(V, V);
+    circle_xor8(V, V);
+    xor_blocks(W, U, V, 32);
+    swap_bytes(W, Key);
+    /* Encrypt last 8 bytes with fourth key */
+    gost_enc_with_key(c, Key, H + 24, S + 24);
+    for (i = 0; i < 12; i++)
+        transform_3(S);
+    xor_blocks(S, S, M, 32);
+    transform_3(S);
+    xor_blocks(S, S, H, 32);
+    for (i = 0; i < 61; i++)
+        transform_3(S);
+    memcpy(H, S, 32);
+    return 1;
+}
+
+/*
+ * Initialize gost_hash ctx - cleans up temporary structures and set up
+ * substitution blocks
+ */
+int init_gost_hash_ctx(gost_hash_ctx * ctx,
+                       const gost_subst_block * subst_block)
+{
+    memset(ctx, 0, sizeof(gost_hash_ctx));
+    ctx->cipher_ctx = (gost_ctx *) MYALLOC(sizeof(gost_ctx));
+    if (!ctx->cipher_ctx) {
+        return 0;
+    }
+    gost_init(ctx->cipher_ctx, subst_block);
+    return 1;
+}
+
+/*
+ * Free cipher CTX if it is dynamically allocated. Do not use
+ * if cipher ctx is statically allocated as in OpenSSL implementation of
+ * GOST hash algroritm
+ *
+ */
+void done_gost_hash_ctx(gost_hash_ctx * ctx)
+{
+    /*
+     * No need to use gost_destroy, because cipher keys are not really secret
+     * when hashing
+     */
+    MYFREE(ctx->cipher_ctx);
+}
+
+/*
+ * reset state of hash context to begin hashing new message
+ */
+int start_hash(gost_hash_ctx * ctx)
+{
+    if (!ctx->cipher_ctx)
+        return 0;
+    memset(&(ctx->H), 0, 32);
+    memset(&(ctx->S), 0, 32);
+    ctx->len = 0L;
+    ctx->left = 0;
+    return 1;
+}
+
+/*
+ * Hash block of arbitrary length
+ *
+ *
+ */
+int hash_block(gost_hash_ctx * ctx, const byte * block, size_t length)
+{
+    if (ctx->left) {
+        /*
+         * There are some bytes from previous step
+         */
+        unsigned int add_bytes = 32 - ctx->left;
+        if (add_bytes > length) {
+            add_bytes = length;
+        }
+        memcpy(&(ctx->remainder[ctx->left]), block, add_bytes);
+        ctx->left += add_bytes;
+        if (ctx->left < 32) {
+            return 1;
+        }
+        block += add_bytes;
+        length -= add_bytes;
+        hash_step(ctx->cipher_ctx, ctx->H, ctx->remainder);
+        add_blocks(32, ctx->S, ctx->remainder);
+        ctx->len += 32;
+        ctx->left = 0;
+    }
+    while (length >= 32) {
+        hash_step(ctx->cipher_ctx, ctx->H, block);
+
+        add_blocks(32, ctx->S, block);
+        ctx->len += 32;
+        block += 32;
+        length -= 32;
+    }
+    if (length) {
+        memcpy(ctx->remainder, block, ctx->left = length);
+    }
+    return 1;
+}
+
+/*
+ * Compute hash value from current state of ctx
+ * state of hash ctx becomes invalid and cannot be used for further
+ * hashing.
+ */
+int finish_hash(gost_hash_ctx * ctx, byte * hashval)
+{
+    byte buf[32];
+    byte H[32];
+    byte S[32];
+    ghosthash_len fin_len = ctx->len;
+    byte *bptr;
+    memcpy(H, ctx->H, 32);
+    memcpy(S, ctx->S, 32);
+    if (ctx->left) {
+        memset(buf, 0, 32);
+        memcpy(buf, ctx->remainder, ctx->left);
+        hash_step(ctx->cipher_ctx, H, buf);
+        add_blocks(32, S, buf);
+        fin_len += ctx->left;
+    }
+    memset(buf, 0, 32);
+    bptr = buf;
+    fin_len <<= 3;              /* Hash length in BITS!! */
+    while (fin_len > 0) {
+        *(bptr++) = (byte) (fin_len & 0xFF);
+        fin_len >>= 8;
+    };
+    hash_step(ctx->cipher_ctx, H, buf);
+    hash_step(ctx->cipher_ctx, H, S);
+    memcpy(hashval, H, 32);
+    return 1;
+}
diff --git a/gosthash.h b/gosthash.h
new file mode 100644 (file)
index 0000000..003e668
--- /dev/null
@@ -0,0 +1,52 @@
+/**********************************************************************
+ *                          gosthash.h                                *
+ *             Copyright (c) 2005-2006 Cryptocom LTD                  *
+ *       This file is distributed under the same license as OpenSSL   *
+ *                                                                    *
+ *    Declaration of GOST R 34.11-94 hash functions                   *
+ *       uses  and gost89.h Doesn't need OpenSSL                      *
+ **********************************************************************/
+#ifndef GOSTHASH_H
+# define GOSTHASH_H
+# include "gost89.h"
+# include <stdlib.h>
+
+# if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__)
+typedef __int64 ghosthash_len;
+# elif defined(__arch64__)
+typedef long ghosthash_len;
+# else
+typedef long long ghosthash_len;
+# endif
+
+typedef struct gost_hash_ctx {
+    ghosthash_len len;
+    gost_ctx *cipher_ctx;
+    int left;
+    byte H[32];
+    byte S[32];
+    byte remainder[32];
+} gost_hash_ctx;
+
+/* Initalizes gost hash ctx, including creation of gost cipher ctx */
+
+int init_gost_hash_ctx(gost_hash_ctx * ctx,
+                       const gost_subst_block * subst_block);
+void done_gost_hash_ctx(gost_hash_ctx * ctx);
+
+/*
+ * Cleans up all fields, except cipher ctx preparing ctx for computing of new
+ * hash value
+ */
+int start_hash(gost_hash_ctx * ctx);
+
+/* Hashes block of data */
+int hash_block(gost_hash_ctx * ctx, const byte * block, size_t length);
+
+/*
+ * Finalizes computation of hash and fills buffer (which should be at least
+ * 32 bytes long) with value of computed hash.
+ */
+int finish_hash(gost_hash_ctx * ctx, byte * hashval);
+
+#endif
diff --git a/gostsum.c b/gostsum.c
new file mode 100644 (file)
index 0000000..1021848
--- /dev/null
+++ b/gostsum.c
@@ -0,0 +1,187 @@
+/**********************************************************************
+ *                        gostsum.c                                   *
+ *             Copyright (c) 2005-2006 Cryptocom LTD                  *
+ *         This file is distributed under the same license as OpenSSL *
+ *                                                                    *
+ *        Almost drop-in replacement for md5sum and sha1sum           *
+ *          which computes GOST R 34.11-94 hashsum instead            *
+ *                                                                    *
+ **********************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <string.h>
+#include "gosthash.h"
+#define BUF_SIZE 262144
+int hash_file(gost_hash_ctx * ctx, char *filename, char *sum, int mode);
+int hash_stream(gost_hash_ctx * ctx, int fd, char *sum);
+int get_line(FILE *f, char *hash, char *filename);
+void help()
+{
+    fprintf(stderr, "gostsum [-bvt] [-c [file]]| [files]\n"
+            "\t-c check message digests (default is generate)\n"
+            "\t-v verbose, print file names when checking\n"
+            "\t-b read files in binary mode\n"
+            "\t-t use test GOST paramset (default is CryptoPro paramset)\n"
+            "The input for -c should be the list of message digests and file names\n"
+            "that is printed on stdout by this program when it generates digests.\n");
+    exit(3);
+}
+
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+
+int main(int argc, char **argv)
+{
+    int c, i;
+    int verbose = 0;
+    int errors = 0;
+    int open_mode = O_RDONLY;
+    gost_subst_block *b = &GostR3411_94_CryptoProParamSet;
+    FILE *check_file = NULL;
+    gost_hash_ctx ctx;
+
+    while ((c = getopt(argc, argv, "bc::tv")) != -1) {
+        switch (c) {
+        case 'v':
+            verbose = 1;
+            break;
+        case 't':
+            b = &GostR3411_94_TestParamSet;
+            break;
+        case 'b':
+            open_mode |= O_BINARY;
+            break;
+        case 'c':
+            if (optarg) {
+                check_file = fopen(optarg, "r");
+                if (!check_file) {
+                    perror(optarg);
+                    exit(2);
+                }
+            } else {
+                check_file = stdin;
+            }
+            break;
+        default:
+            fprintf(stderr, "invalid option %c", optopt);
+            help();
+        }
+    }
+    init_gost_hash_ctx(&ctx, b);
+    if (check_file) {
+        char inhash[65], calcsum[65], filename[PATH_MAX];
+        int failcount = 0, count = 0;;
+        if (check_file == stdin && optind < argc) {
+            check_file = fopen(argv[optind], "r");
+            if (!check_file) {
+                perror(argv[optind]);
+                exit(2);
+            }
+        }
+        while (get_line(check_file, inhash, filename)) {
+            if (!hash_file(&ctx, filename, calcsum, open_mode)) {
+                exit(2);
+            }
+            count++;
+            if (!strncmp(calcsum, inhash, 65)) {
+                if (verbose) {
+                    fprintf(stderr, "%s\tOK\n", filename);
+                }
+            } else {
+                if (verbose) {
+                    fprintf(stderr, "%s\tFAILED\n", filename);
+                } else {
+                    fprintf(stderr,
+                            "%s: GOST hash sum check failed for '%s'\n",
+                            argv[0], filename);
+                }
+                failcount++;
+            }
+        }
+        if (verbose && failcount) {
+            fprintf(stderr,
+                    "%s: %d of %d file(f) failed GOST hash sum check\n",
+                    argv[0], failcount, count);
+        }
+        exit(failcount ? 1 : 0);
+    }
+    if (optind == argc) {
+        char sum[65];
+        if (!hash_stream(&ctx, fileno(stdin), sum)) {
+            perror("stdin");
+            exit(1);
+        }
+        printf("%s -\n", sum);
+        exit(0);
+    }
+    for (i = optind; i < argc; i++) {
+        char sum[65];
+        if (!hash_file(&ctx, argv[i], sum, open_mode)) {
+            errors++;
+        } else {
+            printf("%s %s\n", sum, argv[i]);
+        }
+    }
+    exit(errors ? 1 : 0);
+}
+
+int hash_file(gost_hash_ctx * ctx, char *filename, char *sum, int mode)
+{
+    int fd;
+    if ((fd = open(filename, mode)) < 0) {
+        perror(filename);
+        return 0;
+    }
+    if (!hash_stream(ctx, fd, sum)) {
+        perror(filename);
+        return 0;
+    }
+    close(fd);
+    return 1;
+}
+
+int hash_stream(gost_hash_ctx * ctx, int fd, char *sum)
+{
+    unsigned char buffer[BUF_SIZE];
+    ssize_t bytes;
+    int i;
+    start_hash(ctx);
+    while ((bytes = read(fd, buffer, BUF_SIZE)) > 0) {
+        hash_block(ctx, buffer, bytes);
+    }
+    if (bytes < 0) {
+        return 0;
+    }
+    finish_hash(ctx, buffer);
+    for (i = 0; i < 32; i++) {
+        sprintf(sum + 2 * i, "%02x", buffer[31 - i]);
+    }
+    return 1;
+}
+
+int get_line(FILE *f, char *hash, char *filename)
+{
+    int i;
+    if (fread(hash, 1, 64, f) < 64)
+        return 0;
+    hash[64] = 0;
+    for (i = 0; i < 64; i++) {
+        if (hash[i] < '0' || (hash[i] > '9' && hash[i] < 'A')
+            || (hash[i] > 'F' && hash[i] < 'a') || hash[i] > 'f') {
+            fprintf(stderr, "Not a hash value '%s'\n", hash);
+            return 0;
+        }
+    }
+    if (fgetc(f) != ' ') {
+        fprintf(stderr, "Malformed input line\n");
+        return 0;
+    }
+    i = strlen(fgets(filename, PATH_MAX, f));
+    while (filename[--i] == '\n' || filename[i] == '\r')
+        filename[i] = 0;
+    return 1;
+}