From: Sergey Matveev Date: Sun, 29 Apr 2018 20:01:47 +0000 (+0300) Subject: 34.11-2012 PBKDF2 X-Git-Tag: 3.15~32 X-Git-Url: http://www.git.cypherpunks.ru/?p=pygost.git;a=commitdiff_plain;h=212bb9852fd66cf0b1ac054ed36408fd29752c62 34.11-2012 PBKDF2 --- diff --git a/NEWS b/NEWS index 203723f..6feb6b8 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,6 @@ +3.8: + * 34.11-2012 based PBKDF2 function added + 3.7: Fixed 34.13-2015 OFB bug with IVs longer than 2 blocks. diff --git a/README b/README index bb03586..1594ebf 100644 --- a/README +++ b/README @@ -8,6 +8,7 @@ GOST is GOvernment STandard of Russian Federation (and Soviet Union). * GOST R 34.11-94 hash function (RFC 5831) * GOST R 34.11-94 based PBKDF2 function * GOST R 34.11-2012 Стрибог (Streebog) hash function (RFC 6986) +* GOST R 34.11-2012 based PBKDF2 function (Р 50.1.111-2016) * GOST R 34.10-2001 (RFC 5832) public key signature function * GOST R 34.10-2012 (RFC 7091) public key signature function * various 34.10 curve parameters included diff --git a/VERSION b/VERSION index 475ba51..cc1923a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.7 +3.8 diff --git a/pygost/gost34112012512.py b/pygost/gost34112012512.py index 961a459..707397d 100644 --- a/pygost/gost34112012512.py +++ b/pygost/gost34112012512.py @@ -5,6 +5,7 @@ taken according to specification's terminology. """ from pygost.gost34112012 import GOST34112012 +from pygost.pbkdf2 import pbkdf2 as pbkdf2_base class GOST34112012512(GOST34112012): @@ -14,3 +15,7 @@ class GOST34112012512(GOST34112012): def new(data=b''): return GOST34112012512(data) + + +def pbkdf2(password, salt, iterations, dklen): + return pbkdf2_base(GOST34112012512, password, salt, iterations, dklen) diff --git a/pygost/gost341194.py b/pygost/gost341194.py index c276628..0994437 100644 --- a/pygost/gost341194.py +++ b/pygost/gost341194.py @@ -21,6 +21,7 @@ taken according to specification's terminology. """ from copy import copy +from functools import partial from struct import pack from pygost.gost28147 import addmod @@ -29,10 +30,9 @@ from pygost.gost28147 import encrypt from pygost.gost28147 import ns2block from pygost.gost28147 import validate_sbox from pygost.iface import PEP247 -from pygost.utils import bytes2long +from pygost.pbkdf2 import pbkdf2 as pbkdf2_base from pygost.utils import hexdec from pygost.utils import hexenc -from pygost.utils import long2bytes from pygost.utils import strxor from pygost.utils import xrange # pylint: disable=redefined-builtin @@ -187,34 +187,8 @@ def new(data=b"", sbox=DEFAULT_SBOX): return GOST341194(data, sbox) -# This implementation is based on Python 3.5.2 source code's one. -# PyGOST does not register itself in hashlib anyway, so use it instead. -def pbkdf2(password, salt, iterations, dklen): - """PBKDF2 implementation for GOST R 34.11-94 +PBKDF2_HASHER = partial(GOST341194, sbox="GostR3411_94_CryptoProParamSet") - Based on http://tc26.ru/methods/containers_v1/Addition_to_PKCS5_v1_0.pdf - """ - inner = GOST341194(sbox="GostR3411_94_CryptoProParamSet") - outer = GOST341194(sbox="GostR3411_94_CryptoProParamSet") - password = password + b"\x00" * (inner.block_size - len(password)) - inner.update(strxor(password, len(password) * b"\x36")) - outer.update(strxor(password, len(password) * b"\x5C")) - - def prf(msg): - icpy = inner.copy() - ocpy = outer.copy() - icpy.update(msg) - ocpy.update(icpy.digest()) - return ocpy.digest() - - dkey = b'' - loop = 1 - while len(dkey) < dklen: - prev = prf(salt + long2bytes(loop, 4)) - rkey = bytes2long(prev) - for _ in xrange(iterations - 1): - prev = prf(prev) - rkey ^= bytes2long(prev) - loop += 1 - dkey += long2bytes(rkey, inner.digest_size) - return dkey[:dklen] + +def pbkdf2(password, salt, iterations, dklen): + return pbkdf2_base(PBKDF2_HASHER, password, salt, iterations, dklen) diff --git a/pygost/pbkdf2.py b/pygost/pbkdf2.py new file mode 100644 index 0000000..33df6fe --- /dev/null +++ b/pygost/pbkdf2.py @@ -0,0 +1,41 @@ +# coding: utf-8 +""" PBKDF2 implementation suitable for GOST R 34.11-94/34.11-2012. + +This implementation is based on Python 3.5.2 source code's one. +PyGOST does not register itself in hashlib anyway, so use it instead. +""" + + +from pygost.utils import bytes2long +from pygost.utils import long2bytes +from pygost.utils import strxor +from pygost.utils import xrange # pylint: disable=redefined-builtin + + +def pbkdf2(hasher, password, salt, iterations, dklen): + """PBKDF2 implementation suitable for GOST R 34.11-94/34.11-2012 + """ + inner = hasher() + outer = hasher() + password = password + b"\x00" * (inner.block_size - len(password)) + inner.update(strxor(password, len(password) * b"\x36")) + outer.update(strxor(password, len(password) * b"\x5C")) + + def prf(msg): + icpy = inner.copy() + ocpy = outer.copy() + icpy.update(msg) + ocpy.update(icpy.digest()) + return ocpy.digest() + + dkey = b'' + loop = 1 + while len(dkey) < dklen: + prev = prf(salt + long2bytes(loop, 4)) + rkey = bytes2long(prev) + for _ in xrange(iterations - 1): + prev = prf(prev) + rkey ^= bytes2long(prev) + loop += 1 + dkey += long2bytes(rkey, inner.digest_size) + return dkey[:dklen] diff --git a/pygost/test_gost34112012.py b/pygost/test_gost34112012.py index ac498c7..562be12 100644 --- a/pygost/test_gost34112012.py +++ b/pygost/test_gost34112012.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from unittest import skip from unittest import TestCase import hmac @@ -22,7 +23,9 @@ from pygost import gost34112012256 from pygost import gost34112012512 from pygost.gost34112012256 import GOST34112012256 from pygost.gost34112012512 import GOST34112012512 +from pygost.gost34112012512 import pbkdf2 from pygost.utils import hexdec +from pygost.utils import hexenc class TestCopy(TestCase): @@ -83,3 +86,49 @@ class TestVectors(TestCase): GOST34112012256(m).digest(), hexdec("508f7e553c06501d749a66fc28c6cac0b005746d97537fa85d9e40904efed29d")[::-1] ) + + +class TestPBKDF2(TestCase): + """http://tc26.ru/.../R_50.1.111-2016.pdf + """ + def test_1(self): + self.assertEqual( + hexenc(pbkdf2(b"password", b"salt", 1, 64)), + "64770af7f748c3b1c9ac831dbcfd85c26111b30a8a657ddc3056b80ca73e040d2854fd36811f6d825cc4ab66ec0a68a490a9e5cf5156b3a2b7eecddbf9a16b47", + ) + + def test_2(self): + self.assertEqual( + hexenc(pbkdf2(b"password", b"salt", 2, 64)), + "5a585bafdfbb6e8830d6d68aa3b43ac00d2e4aebce01c9b31c2caed56f0236d4d34b2b8fbd2c4e89d54d46f50e47d45bbac301571743119e8d3c42ba66d348de", + ) + + def test_3(self): + self.assertEqual( + hexenc(pbkdf2(b"password", b"salt", 4096, 64)), + "e52deb9a2d2aaff4e2ac9d47a41f34c20376591c67807f0477e32549dc341bc7867c09841b6d58e29d0347c996301d55df0d34e47cf68f4e3c2cdaf1d9ab86c3", + ) + + @skip("it takes too long") + def test_4(self): + self.assertEqual( + hexenc(pbkdf2(b"password", b"salt", 1677216, 64)), + "49e4843bba76e300afe24c4d23dc7392def12f2c0e244172367cd70a8982ac361adb601c7e2a314e8cb7b1e9df840e36ab5615be5d742b6cf203fb55fdc48071", + ) + + def test_5(self): + self.assertEqual( + hexenc(pbkdf2( + b"passwordPASSWORDpassword", + b"saltSALTsaltSALTsaltSALTsaltSALTsalt", + 4096, + 100, + )), + "b2d8f1245fc4d29274802057e4b54e0a0753aa22fc53760b301cf008679e58fe4bee9addcae99ba2b0b20f431a9c5e50f395c89387d0945aedeca6eb4015dfc2bd2421ee9bb71183ba882ceebfef259f33f9e27dc6178cb89dc37428cf9cc52a2baa2d3a", + ) + + def test_6(self): + self.assertEqual( + hexenc(pbkdf2(b"pass\x00word", b"sa\x00lt", 4096, 64)), + "50df062885b69801a3c10248eb0a27ab6e522ffeb20c991c660f001475d73a4e167f782c18e97e92976d9c1d970831ea78ccb879f67068cdac1910740844e830", + ) diff --git a/www.texi b/www.texi index 22db8c2..93c27ca 100644 --- a/www.texi +++ b/www.texi @@ -30,6 +30,7 @@ Currently supported algorithms are: @item GOST R 34.11-94 based @url{https://en.wikipedia.org/wiki/PBKDF2, PBKDF2} function @item GOST R 34.11-2012 Стрибог (Streebog) hash function (@url{https://tools.ietf.org/html/rfc6986.html, RFC 6986}) +@item GOST R 34.11-2012 based PBKDF2 function (Р 50.1.111-2016) @item GOST R 34.10-2001 (@url{https://tools.ietf.org/html/rfc5832.html, RFC 5832}) public key signature function @@ -90,6 +91,11 @@ mailing list. Announcements also go to this mailing list. @unnumbered News @table @strong +@item 3.8 + @itemize + @item 34.11-2012 based PBKDF2 function added + @end itemize + @item 3.7 Fixed 34.13-2015 OFB bug with IVs longer than 2 blocks. @@ -194,11 +200,11 @@ No additional dependencies except Python 2.7/3.x interpreter are required. Preferable way is to download tarball with the signature: @verbatim -% wget http://pygost.cypherpunks.ru/pygost-3.7.tar.xz -% wget http://pygost.cypherpunks.ru/pygost-3.7.tar.xz.sig -% gpg --verify pygost-3.7.tar.xz.sig pygost-3.7.tar.xz -% xz -d < pygost-3.7.tar.xz | tar xf - -% cd pygost-3.7 +% wget http://pygost.cypherpunks.ru/pygost-3.8.tar.xz +% wget http://pygost.cypherpunks.ru/pygost-3.8.tar.xz.sig +% gpg --verify pygost-3.8.tar.xz.sig pygost-3.8.tar.xz +% xz -d < pygost-3.8.tar.xz | tar xf - +% cd pygost-3.8 % python setup.py install @end verbatim