From c98ba9d03213d0c63d6874539d59f7b55fbc3fae Mon Sep 17 00:00:00 2001 From: Dmitry Belyavsky Date: Fri, 14 Aug 2015 20:50:01 +0300 Subject: [PATCH] Initial commit --- Makefile | 276 ++++++++++++++ README.gost | 300 +++++++++++++++ e_gost_err.c | 221 +++++++++++ e_gost_err.h | 158 ++++++++ e_gost_err.proto | 61 +++ gost.ec | 5 + gost2001.c | 466 +++++++++++++++++++++++ gost2001_keyx.c | 292 +++++++++++++++ gost2001_keyx.h | 10 + gost89.c | 576 +++++++++++++++++++++++++++++ gost89.h | 98 +++++ gost94_keyx.c | 280 ++++++++++++++ gost_ameth.c | 943 +++++++++++++++++++++++++++++++++++++++++++++++ gost_asn1.c | 56 +++ gost_crypt.c | 623 +++++++++++++++++++++++++++++++ gost_ctl.c | 93 +++++ gost_eng.c | 281 ++++++++++++++ gost_keywrap.c | 106 ++++++ gost_keywrap.h | 56 +++ gost_lcl.h | 229 ++++++++++++ gost_md.c | 76 ++++ gost_params.c | 207 +++++++++++ gost_params.h | 34 ++ gost_pmeth.c | 621 +++++++++++++++++++++++++++++++ gost_sign.c | 373 +++++++++++++++++++ gosthash.c | 268 ++++++++++++++ gosthash.h | 52 +++ gostsum.c | 187 ++++++++++ 28 files changed, 6948 insertions(+) create mode 100644 Makefile create mode 100644 README.gost create mode 100644 e_gost_err.c create mode 100644 e_gost_err.h create mode 100644 e_gost_err.proto create mode 100644 gost.ec create mode 100644 gost2001.c create mode 100644 gost2001_keyx.c create mode 100644 gost2001_keyx.h create mode 100644 gost89.c create mode 100644 gost89.h create mode 100644 gost94_keyx.c create mode 100644 gost_ameth.c create mode 100644 gost_asn1.c create mode 100644 gost_crypt.c create mode 100644 gost_ctl.c create mode 100644 gost_eng.c create mode 100644 gost_keywrap.c create mode 100644 gost_keywrap.h create mode 100644 gost_lcl.h create mode 100644 gost_md.c create mode 100644 gost_params.c create mode 100644 gost_params.h create mode 100644 gost_pmeth.c create mode 100644 gost_sign.c create mode 100644 gosthash.c create mode 100644 gosthash.h create mode 100644 gostsum.c diff --git a/Makefile b/Makefile new file mode 100644 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 index 0000000..c96cccc --- /dev/null +++ b/README.gost @@ -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 + Encryption with GOST 28147 CNT mode + openssl enc -gost89-cnt -out encrypted-file -in plain-text-file -k + + +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 index 0000000..80ef58f --- /dev/null +++ b/e_gost_err.c @@ -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 +#include +#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 index 0000000..a2018ec --- /dev/null +++ b/e_gost_err.h @@ -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 index 0000000..c57bd1b --- /dev/null +++ b/e_gost_err.proto @@ -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 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 index 0000000..9536295 --- /dev/null +++ b/gost2001.c @@ -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 +#include +#include +#include +#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 index 0000000..db1bdc1 --- /dev/null +++ b/gost2001_keyx.c @@ -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 +#include +#include +#include +#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 index 0000000..2d29113 --- /dev/null +++ b/gost2001_keyx.h @@ -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 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 +#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 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 index 0000000..ce57f17 --- /dev/null +++ b/gost94_keyx.c @@ -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 +#include +#include +#include +#include + +#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 index 0000000..b7c5354 --- /dev/null +++ b/gost_ameth.c @@ -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 +#include +#include +#include +#include +#include +#ifndef OPENSSL_NO_CMS +# include +#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, ¶ms->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, ""); + 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, "= 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 index 0000000..1168633 --- /dev/null +++ b/gost_asn1.c @@ -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 +#include +#include +#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 index 0000000..2bbdc6c --- /dev/null +++ b/gost_crypt.c @@ -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 +#include "gost89.h" +#include +#include "e_gost_err.h" +#include "gost_lcl.h" + +#if !defined(CCGOST_DEBUG) && !defined(DEBUG) +# ifndef NDEBUG +# define NDEBUG +# endif +#endif +#include + +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 index 0000000..6c93c45 --- /dev/null +++ b/gost_ctl.c @@ -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 +#include +#include +#include +#include +#include +#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 index 0000000..5924791 --- /dev/null +++ b/gost_eng.c @@ -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 +#include +#include +#include +#include +#include +#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 index 0000000..502a83c --- /dev/null +++ b/gost_keywrap.c @@ -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 +#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 index 0000000..7def4c9 --- /dev/null +++ b/gost_keywrap.h @@ -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 +# 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 index 0000000..3a2c7d5 --- /dev/null +++ b/gost_lcl.h @@ -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 +# include +# include +# include +# include +# include +# include +# 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 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 +#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 index 0000000..0411534 --- /dev/null +++ b/gost_params.c @@ -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 +/* 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 index 0000000..0773cbf --- /dev/null +++ b/gost_params.h @@ -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 index 0000000..4a79a85 --- /dev/null +++ b/gost_pmeth.c @@ -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 +#include +#include +#include /* For string_to_hex */ +#include +#include +#include +#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 index 0000000..07ad921 --- /dev/null +++ b/gost_sign.c @@ -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 +#include +#include +#include +#include +#include + +#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 index 0000000..72faa24 --- /dev/null +++ b/gosthash.c @@ -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 + +#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 +# 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 index 0000000..003e668 --- /dev/null +++ b/gosthash.h @@ -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 + +# 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 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 +#include +#include +#include +#include +#include +#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; +} -- 2.39.2