[Python-modules-commits] [python-pyotp] 01/02: Imported Upstream version 2.1.1

Hugo Lefeuvre hle at moszumanska.debian.org
Tue Jul 5 15:01:53 UTC 2016


This is an automated email from the git hooks/post-receive script.

hle pushed a commit to branch master
in repository python-pyotp.

commit 8714c54ef74006735e1c78585c55dcd5559f9f75
Author: Hugo Lefeuvre <hle at debian.org>
Date:   Tue Jul 5 16:58:52 2016 +0200

    Imported Upstream version 2.1.1
---
 PKG-INFO                        |  2 +-
 requirements.txt                |  1 +
 setup.py                        |  2 +-
 src/pyotp.egg-info/PKG-INFO     |  2 +-
 src/pyotp.egg-info/SOURCES.txt  |  1 +
 src/pyotp.egg-info/requires.txt |  1 +
 src/pyotp/__init__.py           |  3 ---
 src/pyotp/hotp.py               |  2 +-
 src/pyotp/otp.py                |  5 ++++-
 src/pyotp/totp.py               | 16 ++++++++++----
 src/pyotp/utils.py              |  5 ++++-
 test.py                         | 49 +++++++++++++++++++++++++++++------------
 12 files changed, 62 insertions(+), 27 deletions(-)

diff --git a/PKG-INFO b/PKG-INFO
index c77fda1..301da3e 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: pyotp
-Version: 2.0.1
+Version: 2.1.1
 Summary: Python One Time Password Library
 Home-page: https://github.com/pyotp/pyotp
 Author: PyOTP contributors
diff --git a/requirements.txt b/requirements.txt
index e69de29..970c6de 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -0,0 +1 @@
+future==0.15.2
diff --git a/setup.py b/setup.py
index 06ddcb6..1d7ed2c 100755
--- a/setup.py
+++ b/setup.py
@@ -7,7 +7,7 @@ install_requires = [line.rstrip() for line in open(os.path.join(os.path.dirname(
 
 setup(
     name='pyotp',
-    version='2.0.1',
+    version='2.1.1',
     url='https://github.com/pyotp/pyotp',
     license='BSD License',
     author='PyOTP contributors',
diff --git a/src/pyotp.egg-info/PKG-INFO b/src/pyotp.egg-info/PKG-INFO
index c77fda1..301da3e 100644
--- a/src/pyotp.egg-info/PKG-INFO
+++ b/src/pyotp.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: pyotp
-Version: 2.0.1
+Version: 2.1.1
 Summary: Python One Time Password Library
 Home-page: https://github.com/pyotp/pyotp
 Author: PyOTP contributors
diff --git a/src/pyotp.egg-info/SOURCES.txt b/src/pyotp.egg-info/SOURCES.txt
index 13f1e12..2ded9a4 100644
--- a/src/pyotp.egg-info/SOURCES.txt
+++ b/src/pyotp.egg-info/SOURCES.txt
@@ -14,4 +14,5 @@ src/pyotp.egg-info/PKG-INFO
 src/pyotp.egg-info/SOURCES.txt
 src/pyotp.egg-info/dependency_links.txt
 src/pyotp.egg-info/not-zip-safe
+src/pyotp.egg-info/requires.txt
 src/pyotp.egg-info/top_level.txt
\ No newline at end of file
diff --git a/src/pyotp.egg-info/requires.txt b/src/pyotp.egg-info/requires.txt
new file mode 100644
index 0000000..970c6de
--- /dev/null
+++ b/src/pyotp.egg-info/requires.txt
@@ -0,0 +1 @@
+future==0.15.2
diff --git a/src/pyotp/__init__.py b/src/pyotp/__init__.py
index 2c661b2..deafdc6 100644
--- a/src/pyotp/__init__.py
+++ b/src/pyotp/__init__.py
@@ -7,9 +7,6 @@ from pyotp.otp import OTP
 from pyotp.totp import TOTP
 from . import utils
 
-VERSION = '1.4.2'
-
-
 def random_base32(length=16, random=_random.SystemRandom(),
                   chars=list('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')):
     return ''.join(
diff --git a/src/pyotp/hotp.py b/src/pyotp/hotp.py
index 5a06b1a..603e8a5 100644
--- a/src/pyotp/hotp.py
+++ b/src/pyotp/hotp.py
@@ -2,7 +2,7 @@ from __future__ import print_function, unicode_literals, division, absolute_impo
 
 from pyotp.otp import OTP
 from pyotp import utils
-
+from future.builtins import str
 
 class HOTP(OTP):
     def at(self, count):
diff --git a/src/pyotp/otp.py b/src/pyotp/otp.py
index bd61fd0..f3144d1 100644
--- a/src/pyotp/otp.py
+++ b/src/pyotp/otp.py
@@ -3,7 +3,7 @@ from __future__ import print_function, unicode_literals, division, absolute_impo
 import base64
 import hashlib
 import hmac
-
+from future.builtins import str
 
 class OTP(object):
     def __init__(self, s, digits=6, digest=hashlib.sha1):
@@ -46,6 +46,9 @@ class OTP(object):
         return str_code
 
     def byte_secret(self):
+        missing_padding = len(self.secret) % 8
+        if missing_padding != 0:
+            self.secret += '=' * (8 - missing_padding)
         return base64.b32decode(self.secret, casefold=True)
 
     @staticmethod
diff --git a/src/pyotp/totp.py b/src/pyotp/totp.py
index 59aedf6..3026fe0 100644
--- a/src/pyotp/totp.py
+++ b/src/pyotp/totp.py
@@ -5,7 +5,7 @@ import time
 
 from pyotp import utils
 from pyotp.otp import OTP
-
+from future.builtins import str
 
 class TOTP(OTP):
     def __init__(self, *args, **kwargs):
@@ -16,15 +16,16 @@ class TOTP(OTP):
         self.interval = kwargs.pop('interval', 30)
         super(TOTP, self).__init__(*args, **kwargs)
 
-    def at(self, for_time):
+    def at(self, for_time, counter_offset=0):
         """
         Accepts either a Unix timestamp integer or a Time object.
         Time objects will be adjusted to UTC automatically
         @param [Time/Integer] time the time to generate an OTP for
+        @param [Integer] counter_offset an amount of ticks to add to the time counter 
         """
         if not isinstance(for_time, datetime.datetime):
             for_time = datetime.datetime.fromtimestamp(int(for_time))
-        return self.generate_otp(self.timecode(for_time))
+        return self.generate_otp(self.timecode(for_time) + counter_offset)
 
     def now(self):
         """
@@ -33,13 +34,20 @@ class TOTP(OTP):
         """
         return self.generate_otp(self.timecode(datetime.datetime.now()))
 
-    def verify(self, otp, for_time=None):
+    def verify(self, otp, for_time=None, valid_window=0):
         """
         Verifies the OTP passed in against the current time OTP
         @param [String/Integer] otp the OTP to check against
+        @param [Integer] valid_window extends the validity to this many counter ticks before and after the current one
         """
         if for_time is None:
             for_time = datetime.datetime.now()
+        
+        if valid_window:
+            for i in range(-valid_window, valid_window + 1):
+                if utils.strings_equal(str(otp), str(self.at(for_time, i))):
+                    return True
+            return False
 
         return utils.strings_equal(str(otp), str(self.at(for_time)))
 
diff --git a/src/pyotp/utils.py b/src/pyotp/utils.py
index 2280fdf..f816703 100644
--- a/src/pyotp/utils.py
+++ b/src/pyotp/utils.py
@@ -1,11 +1,12 @@
 from __future__ import print_function, unicode_literals, division, absolute_import
 
+import unicodedata
+
 try:
     from urllib.parse import quote
 except ImportError:
     from urllib import quote
 
-
 def build_uri(secret, name, initial_count=None, issuer_name=None):
     """
     Returns the provisioning URI for the OTP; works for either TOTP or HOTP.
@@ -60,6 +61,8 @@ def strings_equal(s1, s2):
     still reveal to a timing attack whether the strings are the same
     length.
     """
+    s1 = unicodedata.normalize('NFKC', s1)
+    s2 = unicodedata.normalize('NFKC', s2)
     try:
         # Python 3.3+ and 2.7.7+ include a timing-attack-resistant
         # comparison function, which is probably more reliable than ours.
diff --git a/test.py b/test.py
index d36557b..3615836 100755
--- a/test.py
+++ b/test.py
@@ -1,4 +1,5 @@
 #!/usr/bin/env python
+# coding: utf-8
 
 from __future__ import print_function, unicode_literals, division, absolute_import
 
@@ -12,9 +13,8 @@ import unittest
 sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
 import pyotp
 
-
 class HOTPExampleValuesFromTheRFC(unittest.TestCase):
-    def testMatchTheRFC(self):
+    def test_match_rfc(self):
         # 12345678901234567890 in Bas32
         # GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ
         hotp = pyotp.HOTP('GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ')
@@ -29,13 +29,13 @@ class HOTPExampleValuesFromTheRFC(unittest.TestCase):
         self.assertEqual(hotp.at(8), '399871')
         self.assertEqual(hotp.at(9), '520489')
 
-    def testVerifyAnOTPAndNowAllowReuse(self):
+    def test_verify_otp_reuse(self):
         hotp = pyotp.HOTP('GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ')
         self.assertTrue(hotp.verify('520489', 9))
         self.assertFalse(hotp.verify('520489', 10))
         self.assertFalse(hotp.verify('520489', 10))
 
-    def testProvisioningURI(self):
+    def test_provisioning_uri(self):
         hotp = pyotp.HOTP('wrn3pqx5uqxqvnqr')
 
         self.assertEqual(
@@ -50,6 +50,13 @@ class HOTPExampleValuesFromTheRFC(unittest.TestCase):
             hotp.provisioning_uri('mark at percival', issuer_name='FooCorp!'),
             'otpauth://hotp/FooCorp%21:mark@percival?secret=wrn3pqx5uqxqvnqr&counter=0&issuer=FooCorp%21')
 
+    def test_other_secret(self):
+        hotp = pyotp.HOTP('N3OVNIBRERIO5OHGVCMDGS4V4RJ3AUZOUN34J6FRM4P6JIFCG3ZA')
+        self.assertEqual(hotp.at(0), '737863')
+        self.assertEqual(hotp.at(1), '390601')
+        self.assertEqual(hotp.at(2), '363354')
+        self.assertEqual(hotp.at(3), '936780')
+        self.assertEqual(hotp.at(4), '654019')
 
 class TOTPExampleValuesFromTheRFC(unittest.TestCase):
     RFC_VALUES = {
@@ -81,7 +88,7 @@ class TOTPExampleValuesFromTheRFC(unittest.TestCase):
         ),
     }
 
-    def testMatchTheRFC(self):
+    def test_match_rfc(self):
         for digest, secret in self.RFC_VALUES:
             totp = pyotp.TOTP(base64.b32encode(secret), 8, digest)
             for utime, code in self.RFC_VALUES[(digest, secret)]:
@@ -90,18 +97,18 @@ class TOTPExampleValuesFromTheRFC(unittest.TestCase):
                 msg %= (value, code, digest().name, utime)
                 self.assertEqual(value, str(code), msg)
 
-    def testMatchTheRFCDigitLength(self):
+    def test_match_rfc_digit_length(self):
         totp = pyotp.TOTP('GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ')
         self.assertEqual(totp.at(1111111111), '050471')
         self.assertEqual(totp.at(1234567890), '005924')
         self.assertEqual(totp.at(2000000000), '279037')
 
-    def testMatchTheGoogleAuthenticatorOutput(self):
+    def test_match_google_authenticator_output(self):
         totp = pyotp.TOTP('wrn3pqx5uqxqvnqr')
         with Timecop(1297553958):
             self.assertEqual(totp.now(), '102705')
 
-    def testValidateATimeBasedOTP(self):
+    def test_validate_totp(self):
         totp = pyotp.TOTP('wrn3pqx5uqxqvnqr')
         with Timecop(1297553958):
             self.assertTrue(totp.verify('102705'))
@@ -109,14 +116,14 @@ class TOTPExampleValuesFromTheRFC(unittest.TestCase):
         with Timecop(1297553958 + 30):
             self.assertFalse(totp.verify('102705'))
 
-    def testValidateATimeBasedOTPWithDigitLength(self):
+    def test_validate_totp_with_digit_length(self):
         totp = pyotp.TOTP('GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ')
         with Timecop(1111111111):
             self.assertTrue(totp.verify('050471'))
         with Timecop(1297553958 + 30):
             self.assertFalse(totp.verify('050471'))
 
-    def testProvisioningURI(self):
+    def test_provisioning_uri(self):
         totp = pyotp.TOTP('wrn3pqx5uqxqvnqr')
         self.assertEqual(
             totp.provisioning_uri('mark at percival'),
@@ -126,13 +133,12 @@ class TOTPExampleValuesFromTheRFC(unittest.TestCase):
             totp.provisioning_uri('mark at percival', issuer_name='FooCorp!'),
             'otpauth://totp/FooCorp%21:mark@percival?secret=wrn3pqx5uqxqvnqr&issuer=FooCorp%21')
 
-    def testRandomKeyGeneration(self):
+    def test_random_key_generation(self):
         self.assertEqual(len(pyotp.random_base32()), 16)
         self.assertEqual(len(pyotp.random_base32(length=20)), 20)
 
-
 class StringComparisonTest(unittest.TestCase):
-    def testComparisons(self):
+    def test_comparisons(self):
         self.assertTrue(pyotp.utils.strings_equal("", ""))
         self.assertTrue(pyotp.utils.strings_equal("a", "a"))
         self.assertTrue(pyotp.utils.strings_equal("a" * 1000, "a" * 1000))
@@ -141,6 +147,22 @@ class StringComparisonTest(unittest.TestCase):
         self.assertFalse(pyotp.utils.strings_equal("a", ""))
         self.assertFalse(pyotp.utils.strings_equal("a" * 999 + "b", "a" * 1000))
 
+    def test_fullwidth_input(self):
+        self.assertTrue(pyotp.utils.strings_equal("xs12345", "xs12345"))
+
+class CounterOffsetTest(unittest.TestCase):
+    def test_counter_offset(self):
+        totp = pyotp.TOTP("ABCDEFGH")
+        self.assertEqual(totp.at(200), "028307")
+        self.assertTrue(totp.at(200, 1), "681610")
+
+class ValidWindowTest(unittest.TestCase):
+    def test_valid_window(self):
+        totp = pyotp.TOTP("ABCDEFGH")
+        self.assertTrue(totp.verify("451564", 200, 1))
+        self.assertTrue(totp.verify("028307", 200, 1))
+        self.assertTrue(totp.verify("681610", 200, 1))
+        self.assertFalse(totp.verify("195979", 200, 1))
 
 class Timecop(object):
     """
@@ -166,6 +188,5 @@ class Timecop(object):
         timecop = self
         return FrozenDateTime
 
-
 if __name__ == '__main__':
     unittest.main()

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-pyotp.git



More information about the Python-modules-commits mailing list