]> Cypherpunks.ru repositories - pygost.git/commitdiff
34.11-2012 PBKDF2
authorSergey Matveev <stargrave@stargrave.org>
Sun, 29 Apr 2018 20:01:47 +0000 (23:01 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Sun, 9 Dec 2018 10:01:00 +0000 (13:01 +0300)
NEWS
README
VERSION
pygost/gost34112012512.py
pygost/gost341194.py
pygost/pbkdf2.py [new file with mode: 0644]
pygost/test_gost34112012.py
www.texi

diff --git a/NEWS b/NEWS
index 203723ff26551e92eb628e1f0b9ed044eaf43c47..6feb6b8158e4936e9a39a51fc93becc5b15ab7a8 100644 (file)
--- 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 bb035862fe9445fafeae4b242fe066ef29d98b35..1594ebfa5e34237d12ac7b2a5cd2a1045530c9ae 100644 (file)
--- 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 475ba515c04b5b7cf67a1517430691febc39a32e..cc1923a40b1a5e8c7fbdb8f9f6c6bbe26c35604b 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.7
+3.8
index 961a459c69334249cfaf477e72c818104cb7313a..707397da0245e5fa5f75e8dc7be5ad5856f107c7 100644 (file)
@@ -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)
index c27662851ccfbd29b609368ba1bcab29224195ce..0994437a41002b9d44e51e631df3b5de00720762 100644 (file)
@@ -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 (file)
index 0000000..33df6fe
--- /dev/null
@@ -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]
index ac498c7a3118eca877c22ace367c418307981879..562be12deb34879dc87487149c97b6be444b3244 100644 (file)
@@ -15,6 +15,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+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",
+        )
index 22db8c27c22535d7aa47b2f9e784653c7093522e..93c27ca31b1b43125ee6e22358814edf434ce989 100644 (file)
--- 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