PyOTP 2.5.0__tar.gz → 2.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- Copyright (C) 2011-2017 Mark Percival <m@mdp.im>,
1
+ Copyright (C) 2011-2021 Mark Percival <m@mdp.im>,
2
2
  Nathan Reynolds <email@nreynolds.co.uk>, Andrey Kislyuk <kislyuk@gmail.com>,
3
3
  and PyOTP contributors
4
4
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.1
2
2
  Name: pyotp
3
- Version: 2.5.0
3
+ Version: 2.6.0
4
4
  Summary: Python One Time Password Library
5
5
  Home-page: https://github.com/pyotp/pyotp
6
6
  Author: PyOTP contributors
@@ -7,7 +7,7 @@ install_requires = [line.rstrip() for line in open(os.path.join(os.path.dirname(
7
7
 
8
8
  setup(
9
9
  name="pyotp",
10
- version="2.5.0",
10
+ version="2.6.0",
11
11
  url="https://github.com/pyotp/pyotp",
12
12
  license="MIT License",
13
13
  author="PyOTP contributors",
@@ -9,9 +9,12 @@ from .otp import OTP as OTP
9
9
  from .totp import TOTP as TOTP
10
10
 
11
11
 
12
- def random_base32(length: int = 26, chars: Sequence[str] = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')) -> str:
13
- if length < 26:
14
- raise ValueError("Secrets should be at least 128 bits")
12
+ def random_base32(length: int = 32, chars: Sequence[str] = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')) -> str:
13
+ # Note: the otpauth scheme DOES NOT use base32 padding for secret lengths not divisible by 8.
14
+ # Some third-party tools have bugs when dealing with such secrets.
15
+ # We might consider warning the user when generating a secret of length not divisible by 8.
16
+ if length < 32:
17
+ raise ValueError("Secrets should be at least 160 bits")
15
18
 
16
19
  return ''.join(
17
20
  random.choice(chars)
@@ -19,9 +22,9 @@ def random_base32(length: int = 26, chars: Sequence[str] = list('ABCDEFGHIJKLMNO
19
22
  )
20
23
 
21
24
 
22
- def random_hex(length: int = 32, chars: Sequence[str] = list('ABCDEF0123456789')) -> str:
23
- if length < 32:
24
- raise ValueError("Secrets should be at least 128 bits")
25
+ def random_hex(length: int = 40, chars: Sequence[str] = list('ABCDEF0123456789')) -> str:
26
+ if length < 40:
27
+ raise ValueError("Secrets should be at least 160 bits")
25
28
  return random_base32(length=length, chars=chars)
26
29
 
27
30
 
@@ -82,7 +85,7 @@ def parse_uri(uri: str) -> OTP:
82
85
  otp_data['interval'] = int(value)
83
86
  elif key == 'counter':
84
87
  otp_data['initial_count'] = int(value)
85
- else:
88
+ elif key != 'image':
86
89
  raise ValueError('{} is not a valid parameter'.format(key))
87
90
 
88
91
  if not secret:
@@ -37,10 +37,11 @@ class OTP(object):
37
37
  return str_code
38
38
 
39
39
  def byte_secret(self) -> bytes:
40
- missing_padding = len(self.secret) % 8
40
+ secret = self.secret
41
+ missing_padding = len(secret) % 8
41
42
  if missing_padding != 0:
42
- self.secret += '=' * (8 - missing_padding)
43
- return base64.b32decode(self.secret, casefold=True)
43
+ secret += '=' * (8 - missing_padding)
44
+ return base64.b32decode(secret, casefold=True)
44
45
 
45
46
  @staticmethod
46
47
  def int_to_bytestring(i: int, padding: int = 8) -> bytes:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.1
2
2
  Name: pyotp
3
- Version: 2.5.0
3
+ Version: 2.6.0
4
4
  Summary: Python One Time Password Library
5
5
  Home-page: https://github.com/pyotp/pyotp
6
6
  Author: PyOTP contributors
@@ -7,7 +7,7 @@ from warnings import warn
7
7
  from urllib.parse import urlparse, parse_qsl
8
8
 
9
9
  sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
10
- import pyotp
10
+ import pyotp # noqa
11
11
 
12
12
 
13
13
  class HOTPExampleValuesFromTheRFC(unittest.TestCase):
@@ -118,6 +118,11 @@ class HOTPExampleValuesFromTheRFC(unittest.TestCase):
118
118
  ).provisioning_uri()
119
119
  )
120
120
 
121
+ code = pyotp.totp.TOTP("S46SQCPPTCNPROMHWYBDCTBZXV")
122
+ self.assertEqual(code.provisioning_uri(), "otpauth://totp/Secret?secret=S46SQCPPTCNPROMHWYBDCTBZXV")
123
+ code.verify("123456")
124
+ self.assertEqual(code.provisioning_uri(), "otpauth://totp/Secret?secret=S46SQCPPTCNPROMHWYBDCTBZXV")
125
+
121
126
  def test_other_secret(self):
122
127
  hotp = pyotp.HOTP(
123
128
  'N3OVNIBRERIO5OHGVCMDGS4V4RJ3AUZOUN34J6FRM4P6JIFCG3ZA')
@@ -291,14 +296,14 @@ class TOTPExampleValuesFromTheRFC(unittest.TestCase):
291
296
  )
292
297
 
293
298
  def test_random_key_generation(self):
294
- self.assertEqual(len(pyotp.random_base32()), 26)
295
- self.assertEqual(len(pyotp.random_base32(length=32)), 32)
296
- self.assertEqual(len(pyotp.random_hex()), 32)
297
- self.assertEqual(len(pyotp.random_hex(length=64)), 64)
299
+ self.assertEqual(len(pyotp.random_base32()), 32)
300
+ self.assertEqual(len(pyotp.random_base32(length=34)), 34)
301
+ self.assertEqual(len(pyotp.random_hex()), 40)
302
+ self.assertEqual(len(pyotp.random_hex(length=42)), 42)
298
303
  with self.assertRaises(ValueError):
299
- pyotp.random_base32(length=25)
304
+ pyotp.random_base32(length=31)
300
305
  with self.assertRaises(ValueError):
301
- pyotp.random_hex(length=31)
306
+ pyotp.random_hex(length=39)
302
307
 
303
308
 
304
309
  class CompareDigestTest(unittest.TestCase):
@@ -412,11 +417,17 @@ class ParseUriTest(unittest.TestCase):
412
417
  self.assertEqual(otp.at(0), '816660')
413
418
  self.assertEqual(otp.at(9000), '524153')
414
419
 
415
- self.assertEqual(otp.provisioning_uri(name='n', issuer_name='i', image='https://test.net/test.png'),
416
- 'otpauth://totp/i:n?secret=GEZDGNBV&issuer=i&algorithm=SHA512&image=https%3A%2F%2Ftest.net%2Ftest.png')
420
+ self.assertEqual(
421
+ otp.provisioning_uri(name='n', issuer_name='i', image='https://test.net/test.png'),
422
+ 'otpauth://totp/i:n?secret=GEZDGNBV&issuer=i&algorithm=SHA512&image=https%3A%2F%2Ftest.net%2Ftest.png'
423
+ )
417
424
  with self.assertRaises(ValueError):
418
425
  otp.provisioning_uri(name='n', issuer_name='i', image='nourl')
419
426
 
427
+ otp = pyotp.parse_uri(otp.provisioning_uri(name='n', issuer_name='i', image='https://test.net/test.png'))
428
+ self.assertEqual(hashlib.sha512, otp.digest)
429
+
430
+
420
431
  class Timecop(object):
421
432
  """
422
433
  Half-assed clone of timecop.rb, just enough to pass our tests.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes