[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