PyOTP 2.2.1__tar.gz → 2.2.5__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,6 +1,6 @@
1
1
  Metadata-Version: 1.1
2
2
  Name: pyotp
3
- Version: 2.2.1
3
+ Version: 2.2.5
4
4
  Summary: Python One Time Password Library
5
5
  Home-page: https://github.com/pyotp/pyotp
6
6
  Author: PyOTP contributors
@@ -20,6 +20,8 @@ Description: PyOTP - The Python One-Time Password Library
20
20
  compatible app. Users can set up auth tokens in their apps easily by using their phone camera to scan `otpauth://
21
21
  <https://github.com/google/google-authenticator/wiki/Key-Uri-Format>`_ QR codes provided by PyOTP.
22
22
 
23
+ We recommend that implementers read the `OWASP Authentication Cheat Sheet <https://www.owasp.org/index.php/Authentication_Cheat_Sheet>`_ and `NIST SP 800-63-3: Digital Authentication Guideline <https://pages.nist.gov/800-63-3/>`_ for a high level overview of authentication best practices.
24
+
23
25
  Quick overview of using One Time Passwords on your phone
24
26
  --------------------------------------------------------
25
27
 
@@ -37,7 +39,7 @@ Description: PyOTP - The Python One-Time Password Library
37
39
  Usage
38
40
  -----
39
41
 
40
- Time based OTPs
42
+ Time-based OTPs
41
43
  ~~~~~~~~~~~~~~~
42
44
  ::
43
45
 
@@ -66,23 +68,29 @@ Description: PyOTP - The Python One-Time Password Library
66
68
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
67
69
  ::
68
70
 
69
- pyotp.random_base32() # returns a 16 character base32 secret. Compatible with Google Authenticator
71
+ pyotp.random_base32() # returns a 16 character base32 secret. Compatible with Google Authenticator and other OTP apps
70
72
 
71
73
  Google Authenticator Compatible
72
74
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
73
75
 
74
- The library works with the Google Authenticator iPhone and Android app, and also includes the ability to generate
75
- provisioning URI's for use with the QR Code scanner built into the app::
76
+ PyOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. PyOTP includes the
77
+ ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps::
78
+
79
+ pyotp.totp.TOTP('JBSWY3DPEHPK3PXP').provisioning_uri("alice@google.com", issuer_name="Secure App")
80
+
81
+ >>> 'otpauth://totp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure+App'
82
+
83
+ pyotp.hotp.HOTP('JBSWY3DPEHPK3PXP').provisioning_uri("alice@google.com", initial_count=0, issuer_name="Secure App")
76
84
 
77
- totp.provisioning_uri("alice@google.com") # => 'otpauth://totp/alice@google.com?secret=JBSWY3DPEHPK3PXP'
78
- hotp.provisioning_uri("alice@google.com", 0) # => 'otpauth://hotp/alice@google.com?secret=JBSWY3DPEHPK3PXP&counter=0'
85
+ >>> 'otpauth://hotp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure+App&counter=0'
79
86
 
80
- This can then be rendered as a QR Code which can then be scanned and added to the users list of OTP credentials.
87
+ This URL can then be rendered as a QR Code (for example, using https://github.com/neocotic/qrious) which can then be scanned
88
+ and added to the users list of OTP credentials.
81
89
 
82
90
  Working example
83
91
  ~~~~~~~~~~~~~~~
84
92
 
85
- Scan the following barcode with your phone, using Google Authenticator
93
+ Scan the following barcode with your phone's OTP app (e.g. Google Authenticator):
86
94
 
87
95
  .. image:: http://chart.apis.google.com/chart?cht=qr&chs=250x250&chl=otpauth%3A%2F%2Ftotp%2Falice%40google.com%3Fsecret%3DJBSWY3DPEHPK3PXP
88
96
 
@@ -96,13 +104,15 @@ Description: PyOTP - The Python One-Time Password Library
96
104
  ~~~~~
97
105
 
98
106
  * `Project home page (GitHub) <https://github.com/pyotp/pyotp>`_
99
- * `Documentation (Read the Docs) <https://pyotp.readthedocs.org/en/latest/>`_
107
+ * `Documentation (Read the Docs) <https://pyotp.readthedocs.io/en/latest/>`_
100
108
  * `Package distribution (PyPI) <https://pypi.python.org/pypi/pyotp>`_
101
109
  * `Change log <https://github.com/pyotp/pyotp/blob/master/Changes.rst>`_
102
110
  * `RFC 4226: HOTP: An HMAC-Based One-Time Password <https://tools.ietf.org/html/rfc4226>`_
103
111
  * `RFC 6238: TOTP: Time-Based One-Time Password Algorithm <https://tools.ietf.org/html/rfc6238>`_
104
112
  * `ROTP <https://github.com/mdp/rotp>`_ - Original Ruby OTP library by `Mark Percival <https://github.com/mdp>`_
105
113
  * `OTPHP <https://github.com/lelag/otphp>`_ - PHP port of ROTP by `Le Lag <https://github.com/lelag>`_
114
+ * `OWASP Authentication Cheat Sheet <https://www.owasp.org/index.php/Authentication_Cheat_Sheet>`_
115
+ * `NIST SP 800-63-3: Digital Authentication Guideline <https://pages.nist.gov/800-63-3/>`_
106
116
 
107
117
  .. image:: https://img.shields.io/travis/pyotp/pyotp.svg
108
118
  :target: https://travis-ci.org/pyotp/pyotp
@@ -113,7 +123,7 @@ Description: PyOTP - The Python One-Time Password Library
113
123
  .. image:: https://img.shields.io/pypi/l/pyotp.svg
114
124
  :target: https://pypi.python.org/pypi/pyotp
115
125
  .. image:: https://readthedocs.org/projects/pyotp/badge/?version=latest
116
- :target: https://pyotp.readthedocs.org/
126
+ :target: https://pyotp.readthedocs.io/
117
127
 
118
128
  Platform: MacOS X
119
129
  Platform: Posix
@@ -12,6 +12,8 @@ Authenticator <https://en.wikipedia.org/wiki/Google_Authenticator>`_, `Authy <ht
12
12
  compatible app. Users can set up auth tokens in their apps easily by using their phone camera to scan `otpauth://
13
13
  <https://github.com/google/google-authenticator/wiki/Key-Uri-Format>`_ QR codes provided by PyOTP.
14
14
 
15
+ We recommend that implementers read the `OWASP Authentication Cheat Sheet <https://www.owasp.org/index.php/Authentication_Cheat_Sheet>`_ and `NIST SP 800-63-3: Digital Authentication Guideline <https://pages.nist.gov/800-63-3/>`_ for a high level overview of authentication best practices.
16
+
15
17
  Quick overview of using One Time Passwords on your phone
16
18
  --------------------------------------------------------
17
19
 
@@ -29,7 +31,7 @@ Installation
29
31
  Usage
30
32
  -----
31
33
 
32
- Time based OTPs
34
+ Time-based OTPs
33
35
  ~~~~~~~~~~~~~~~
34
36
  ::
35
37
 
@@ -58,23 +60,29 @@ Generating a base32 Secret Key
58
60
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
59
61
  ::
60
62
 
61
- pyotp.random_base32() # returns a 16 character base32 secret. Compatible with Google Authenticator
63
+ pyotp.random_base32() # returns a 16 character base32 secret. Compatible with Google Authenticator and other OTP apps
62
64
 
63
65
  Google Authenticator Compatible
64
66
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
65
67
 
66
- The library works with the Google Authenticator iPhone and Android app, and also includes the ability to generate
67
- provisioning URI's for use with the QR Code scanner built into the app::
68
+ PyOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. PyOTP includes the
69
+ ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps::
70
+
71
+ pyotp.totp.TOTP('JBSWY3DPEHPK3PXP').provisioning_uri("alice@google.com", issuer_name="Secure App")
72
+
73
+ >>> 'otpauth://totp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure+App'
74
+
75
+ pyotp.hotp.HOTP('JBSWY3DPEHPK3PXP').provisioning_uri("alice@google.com", initial_count=0, issuer_name="Secure App")
68
76
 
69
- totp.provisioning_uri("alice@google.com") # => 'otpauth://totp/alice@google.com?secret=JBSWY3DPEHPK3PXP'
70
- hotp.provisioning_uri("alice@google.com", 0) # => 'otpauth://hotp/alice@google.com?secret=JBSWY3DPEHPK3PXP&counter=0'
77
+ >>> 'otpauth://hotp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure+App&counter=0'
71
78
 
72
- This can then be rendered as a QR Code which can then be scanned and added to the users list of OTP credentials.
79
+ This URL can then be rendered as a QR Code (for example, using https://github.com/neocotic/qrious) which can then be scanned
80
+ and added to the users list of OTP credentials.
73
81
 
74
82
  Working example
75
83
  ~~~~~~~~~~~~~~~
76
84
 
77
- Scan the following barcode with your phone, using Google Authenticator
85
+ Scan the following barcode with your phone's OTP app (e.g. Google Authenticator):
78
86
 
79
87
  .. image:: http://chart.apis.google.com/chart?cht=qr&chs=250x250&chl=otpauth%3A%2F%2Ftotp%2Falice%40google.com%3Fsecret%3DJBSWY3DPEHPK3PXP
80
88
 
@@ -88,13 +96,15 @@ Links
88
96
  ~~~~~
89
97
 
90
98
  * `Project home page (GitHub) <https://github.com/pyotp/pyotp>`_
91
- * `Documentation (Read the Docs) <https://pyotp.readthedocs.org/en/latest/>`_
99
+ * `Documentation (Read the Docs) <https://pyotp.readthedocs.io/en/latest/>`_
92
100
  * `Package distribution (PyPI) <https://pypi.python.org/pypi/pyotp>`_
93
101
  * `Change log <https://github.com/pyotp/pyotp/blob/master/Changes.rst>`_
94
102
  * `RFC 4226: HOTP: An HMAC-Based One-Time Password <https://tools.ietf.org/html/rfc4226>`_
95
103
  * `RFC 6238: TOTP: Time-Based One-Time Password Algorithm <https://tools.ietf.org/html/rfc6238>`_
96
104
  * `ROTP <https://github.com/mdp/rotp>`_ - Original Ruby OTP library by `Mark Percival <https://github.com/mdp>`_
97
105
  * `OTPHP <https://github.com/lelag/otphp>`_ - PHP port of ROTP by `Le Lag <https://github.com/lelag>`_
106
+ * `OWASP Authentication Cheat Sheet <https://www.owasp.org/index.php/Authentication_Cheat_Sheet>`_
107
+ * `NIST SP 800-63-3: Digital Authentication Guideline <https://pages.nist.gov/800-63-3/>`_
98
108
 
99
109
  .. image:: https://img.shields.io/travis/pyotp/pyotp.svg
100
110
  :target: https://travis-ci.org/pyotp/pyotp
@@ -105,4 +115,4 @@ Links
105
115
  .. image:: https://img.shields.io/pypi/l/pyotp.svg
106
116
  :target: https://pypi.python.org/pypi/pyotp
107
117
  .. image:: https://readthedocs.org/projects/pyotp/badge/?version=latest
108
- :target: https://pyotp.readthedocs.org/
118
+ :target: https://pyotp.readthedocs.io/
@@ -3,10 +3,9 @@ universal = 1
3
3
 
4
4
  [flake8]
5
5
  max-line-length = 120
6
- ignore = E301, E302, E401, E226, F841
6
+ ignore = E301, E302, E305, E401, E226, F841
7
7
 
8
8
  [egg_info]
9
9
  tag_build =
10
10
  tag_date = 0
11
- tag_svn_revision = 0
12
11
 
pyotp-2.2.5/setup.py ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env python
2
+
3
+ import os
4
+ from setuptools import setup
5
+
6
+ install_requires = [line.rstrip() for line in open(os.path.join(os.path.dirname(__file__), "requirements.txt"))]
7
+
8
+ setup(
9
+ name="pyotp",
10
+ version="2.2.5",
11
+ url="https://github.com/pyotp/pyotp",
12
+ license="BSD License",
13
+ author="PyOTP contributors",
14
+ author_email="kislyuk@gmail.com",
15
+ description="Python One Time Password Library",
16
+ long_description=open("README.rst").read(),
17
+ install_requires=install_requires,
18
+ packages=["pyotp"],
19
+ package_dir={"": "src"},
20
+ platforms=["MacOS X", "Posix"],
21
+ zip_safe=False,
22
+ test_suite="test",
23
+ classifiers=[
24
+ "Intended Audience :: Developers",
25
+ "License :: OSI Approved :: BSD License",
26
+ "Operating System :: MacOS :: MacOS X",
27
+ "Operating System :: POSIX",
28
+ "Programming Language :: Python",
29
+ "Programming Language :: Python :: 2.7",
30
+ "Programming Language :: Python :: 3.3",
31
+ "Programming Language :: Python :: 3.4",
32
+ "Topic :: Software Development :: Libraries :: Python Modules"
33
+ ]
34
+ )
@@ -0,0 +1,57 @@
1
+ from __future__ import absolute_import, division, print_function, unicode_literals
2
+
3
+ from . import utils
4
+ from .otp import OTP
5
+ from .compat import str
6
+
7
+ class HOTP(OTP):
8
+ """
9
+ Handler for HMAC-based OTP counters.
10
+ """
11
+ def at(self, count):
12
+ """
13
+ Generates the OTP for the given count.
14
+
15
+ :param count: the OTP HMAC counter
16
+ :type count: int
17
+ :returns: OTP
18
+ :rtype: str
19
+ """
20
+ return self.generate_otp(count)
21
+
22
+ def verify(self, otp, counter):
23
+ """
24
+ Verifies the OTP passed in against the current time OTP.
25
+
26
+ :param otp: the OTP to check against
27
+ :type otp: str
28
+ :param count: the OTP HMAC counter
29
+ :type count: int
30
+ """
31
+ return utils.strings_equal(str(otp), str(self.at(counter)))
32
+
33
+ def provisioning_uri(self, name, initial_count=0, issuer_name=None):
34
+ """
35
+ Returns the provisioning URI for the OTP. This can then be
36
+ encoded in a QR Code and used to provision an OTP app like
37
+ Google Authenticator. See
38
+ https://github.com/google/google-authenticator/wiki/Key-Uri-Format
39
+ for more details.
40
+
41
+ :param name: name of the user account
42
+ :type name: str
43
+ :param initial_count: starting HMAC counter value, defaults to 0
44
+ :type initial_count: int
45
+ :param issuer_name: the name of the OTP issuer; this will be the
46
+ organization title of the OTP entry in Authenticator
47
+ :returns: provisioning URI
48
+ :rtype: str
49
+ """
50
+ return utils.build_uri(
51
+ self.secret,
52
+ name,
53
+ initial_count=initial_count,
54
+ issuer_name=issuer_name,
55
+ algorithm=self.digest().name,
56
+ digits=self.digits
57
+ )
@@ -6,16 +6,17 @@ import hmac
6
6
  from .compat import str
7
7
 
8
8
  class OTP(object):
9
+ """
10
+ Base class for OTP handlers.
11
+ """
9
12
  def __init__(self, s, digits=6, digest=hashlib.sha1):
10
13
  """
11
- @param [String] secret in the form of base32
12
- @option options digits [Integer] (6)
13
- Number of integers in the OTP
14
- Google Authenticate only supports 6 currently
15
- @option options digest [Callable] (hashlib.sha1)
16
- Digest used in the HMAC
17
- Google Authenticate only supports 'sha1' currently
18
- @returns [OTP] OTP instantiation
14
+ :param s: secret in base32 format
15
+ :type s: str
16
+ :param digits: number of integers in the OTP. Some apps expect this to be 6 digits, others support more.
17
+ :type digits: int
18
+ :param digest: digest function to use in the HMAC (expected to be sha1)
19
+ :type digest: callable
19
20
  """
20
21
  self.digits = digits
21
22
  self.digest = digest
@@ -23,10 +24,12 @@ class OTP(object):
23
24
 
24
25
  def generate_otp(self, input):
25
26
  """
26
- @param [Integer] input the number used seed the HMAC
27
- Usually either the counter, or the computed integer
28
- based on the Unix timestamp
27
+ :param input: the HMAC counter value to use as the OTP input.
28
+ Usually either the counter, or the computed integer based on the Unix timestamp
29
+ :type input: int
29
30
  """
31
+ if input < 0:
32
+ raise ValueError('input must be positive integer')
30
33
  hasher = hmac.new(self.byte_secret(), self.int_to_bytestring(input), self.digest)
31
34
  hmac_hash = bytearray(hasher.digest())
32
35
  offset = hmac_hash[-1] & 0xf
@@ -8,20 +8,27 @@ from .otp import OTP
8
8
  from .compat import str
9
9
 
10
10
  class TOTP(OTP):
11
+ """
12
+ Handler for time-based OTP counters.
13
+ """
11
14
  def __init__(self, *args, **kwargs):
12
15
  """
13
- @option options [Integer] interval (30) the time interval in seconds
14
- for OTP This defaults to 30 which is standard.
16
+ :param interval: the time interval in seconds
17
+ for OTP. This defaults to 30.
18
+ :type interval: int
15
19
  """
16
20
  self.interval = kwargs.pop('interval', 30)
17
21
  super(TOTP, self).__init__(*args, **kwargs)
18
22
 
19
23
  def at(self, for_time, counter_offset=0):
20
24
  """
21
- Accepts either a Unix timestamp integer or a Time object.
22
- @param [Time/Integer] time the time to generate an OTP for
23
- @param [Integer] counter_offset an amount of ticks to add to the time
24
- counter
25
+ Accepts either a Unix timestamp integer or a datetime object.
26
+
27
+ :param for_time: the time to generate an OTP for
28
+ :type for_time: int or datetime
29
+ :param counter_offset: the amount of ticks to add to the time counter
30
+ :returns: OTP value
31
+ :rtype: str
25
32
  """
26
33
  if not isinstance(for_time, datetime.datetime):
27
34
  for_time = datetime.datetime.fromtimestamp(int(for_time))
@@ -30,16 +37,24 @@ class TOTP(OTP):
30
37
  def now(self):
31
38
  """
32
39
  Generate the current time OTP
33
- @return [Integer] the OTP as an integer
40
+
41
+ :returns: OTP value
42
+ :rtype: str
34
43
  """
35
44
  return self.generate_otp(self.timecode(datetime.datetime.now()))
36
45
 
37
46
  def verify(self, otp, for_time=None, valid_window=0):
38
47
  """
39
- Verifies the OTP passed in against the current time OTP
40
- @param [String/Integer] otp the OTP to check against
41
- @param [Integer] valid_window extends the validity to this many counter
42
- ticks before and after the current one
48
+ Verifies the OTP passed in against the current time OTP.
49
+
50
+ :param otp: the OTP to check against
51
+ :type otp: str
52
+ :param for_time: Time to check OTP at (defaults to now)
53
+ :type for_time: int or datetime
54
+ :param valid_window: extends the validity to this many counter ticks before and after the current one
55
+ :type valid_window: int
56
+ :returns: True if verification succeeded, False otherwise
57
+ :rtype: bool
43
58
  """
44
59
  if for_time is None:
45
60
  for_time = datetime.datetime.now()
@@ -54,11 +69,18 @@ class TOTP(OTP):
54
69
 
55
70
  def provisioning_uri(self, name, issuer_name=None):
56
71
  """
57
- Returns the provisioning URI for the OTP
58
- This can then be encoded in a QR Code and used
59
- to provision the Google Authenticator app
60
- @param [String] name of the account
61
- @return [String] provisioning uri
72
+ Returns the provisioning URI for the OTP. This can then be
73
+ encoded in a QR Code and used to provision an OTP app like
74
+ Google Authenticator. See
75
+ https://github.com/google/google-authenticator/wiki/Key-Uri-Format
76
+ for more details.
77
+
78
+ :param name: name of the user account
79
+ :type name: str
80
+ :param issuer_name: the name of the OTP issuer; this will be the
81
+ organization title of the OTP entry in Authenticator
82
+ :returns: provisioning URI
83
+ :rtype: str
62
84
  """
63
85
  return utils.build_uri(self.secret, name, issuer_name=issuer_name,
64
86
  algorithm=self.digest().name,
@@ -25,17 +25,25 @@ def build_uri(secret, name, initial_count=None, issuer_name=None,
25
25
  See also:
26
26
  https://github.com/google/google-authenticator/wiki/Key-Uri-Format
27
27
 
28
- @param [String] the hotp/totp secret used to generate the URI
29
- @param [String] name of the account
30
- @param [Integer] initial_count starting counter value, defaults to None.
28
+ :param secret: the hotp/totp secret used to generate the URI
29
+ :type secret: str
30
+ :param name: name of the account
31
+ :type name: str
32
+ :param initial_count: starting counter value, defaults to None.
31
33
  If none, the OTP type will be assumed as TOTP.
32
- @param [String] the name of the OTP issuer; this will be the
34
+ :type initial_count: int
35
+ :param issuer_name: the name of the OTP issuer; this will be the
33
36
  organization title of the OTP entry in Authenticator
34
- @param [String] the algorithm used in the OTP generation.
35
- @param [Integer] the length of the OTP generated code.
36
- @param [Integer] the number of seconds the OTP generator is set to
37
+ :type issuer_name: str
38
+ :param algorithm: the algorithm used in the OTP generation.
39
+ :type algorithm: str
40
+ :param digits: the length of the OTP generated code.
41
+ :type digits: int
42
+ :param period: the number of seconds the OTP generator is set to
37
43
  expire every code.
38
- @return [String] provisioning uri
44
+ :type period: int
45
+ :returns: provisioning uri
46
+ :rtype: str
39
47
  """
40
48
  # initial_count may be 0 as a valid param
41
49
  is_initial_count_present = (initial_count is not None)
@@ -46,14 +54,14 @@ def build_uri(secret, name, initial_count=None, issuer_name=None,
46
54
  is_period_set = (period is not None and period != 30)
47
55
 
48
56
  otp_type = 'hotp' if is_initial_count_present else 'totp'
49
- base_uri = 'otpauth://{}/{}?{}'
57
+ base_uri = 'otpauth://{0}/{1}?{2}'
50
58
 
51
59
  url_args = {'secret': secret}
52
60
 
53
61
  label = quote(name)
54
62
  if issuer_name is not None:
55
63
  label = quote(issuer_name) + ':' + label
56
- url_args['issuer'] = issuer_name
64
+ url_args['issuer'] = quote(issuer_name)
57
65
 
58
66
  if is_initial_count_present:
59
67
  url_args['counter'] = initial_count
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.1
2
2
  Name: pyotp
3
- Version: 2.2.1
3
+ Version: 2.2.5
4
4
  Summary: Python One Time Password Library
5
5
  Home-page: https://github.com/pyotp/pyotp
6
6
  Author: PyOTP contributors
@@ -20,6 +20,8 @@ Description: PyOTP - The Python One-Time Password Library
20
20
  compatible app. Users can set up auth tokens in their apps easily by using their phone camera to scan `otpauth://
21
21
  <https://github.com/google/google-authenticator/wiki/Key-Uri-Format>`_ QR codes provided by PyOTP.
22
22
 
23
+ We recommend that implementers read the `OWASP Authentication Cheat Sheet <https://www.owasp.org/index.php/Authentication_Cheat_Sheet>`_ and `NIST SP 800-63-3: Digital Authentication Guideline <https://pages.nist.gov/800-63-3/>`_ for a high level overview of authentication best practices.
24
+
23
25
  Quick overview of using One Time Passwords on your phone
24
26
  --------------------------------------------------------
25
27
 
@@ -37,7 +39,7 @@ Description: PyOTP - The Python One-Time Password Library
37
39
  Usage
38
40
  -----
39
41
 
40
- Time based OTPs
42
+ Time-based OTPs
41
43
  ~~~~~~~~~~~~~~~
42
44
  ::
43
45
 
@@ -66,23 +68,29 @@ Description: PyOTP - The Python One-Time Password Library
66
68
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
67
69
  ::
68
70
 
69
- pyotp.random_base32() # returns a 16 character base32 secret. Compatible with Google Authenticator
71
+ pyotp.random_base32() # returns a 16 character base32 secret. Compatible with Google Authenticator and other OTP apps
70
72
 
71
73
  Google Authenticator Compatible
72
74
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
73
75
 
74
- The library works with the Google Authenticator iPhone and Android app, and also includes the ability to generate
75
- provisioning URI's for use with the QR Code scanner built into the app::
76
+ PyOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. PyOTP includes the
77
+ ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps::
78
+
79
+ pyotp.totp.TOTP('JBSWY3DPEHPK3PXP').provisioning_uri("alice@google.com", issuer_name="Secure App")
80
+
81
+ >>> 'otpauth://totp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure+App'
82
+
83
+ pyotp.hotp.HOTP('JBSWY3DPEHPK3PXP').provisioning_uri("alice@google.com", initial_count=0, issuer_name="Secure App")
76
84
 
77
- totp.provisioning_uri("alice@google.com") # => 'otpauth://totp/alice@google.com?secret=JBSWY3DPEHPK3PXP'
78
- hotp.provisioning_uri("alice@google.com", 0) # => 'otpauth://hotp/alice@google.com?secret=JBSWY3DPEHPK3PXP&counter=0'
85
+ >>> 'otpauth://hotp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure+App&counter=0'
79
86
 
80
- This can then be rendered as a QR Code which can then be scanned and added to the users list of OTP credentials.
87
+ This URL can then be rendered as a QR Code (for example, using https://github.com/neocotic/qrious) which can then be scanned
88
+ and added to the users list of OTP credentials.
81
89
 
82
90
  Working example
83
91
  ~~~~~~~~~~~~~~~
84
92
 
85
- Scan the following barcode with your phone, using Google Authenticator
93
+ Scan the following barcode with your phone's OTP app (e.g. Google Authenticator):
86
94
 
87
95
  .. image:: http://chart.apis.google.com/chart?cht=qr&chs=250x250&chl=otpauth%3A%2F%2Ftotp%2Falice%40google.com%3Fsecret%3DJBSWY3DPEHPK3PXP
88
96
 
@@ -96,13 +104,15 @@ Description: PyOTP - The Python One-Time Password Library
96
104
  ~~~~~
97
105
 
98
106
  * `Project home page (GitHub) <https://github.com/pyotp/pyotp>`_
99
- * `Documentation (Read the Docs) <https://pyotp.readthedocs.org/en/latest/>`_
107
+ * `Documentation (Read the Docs) <https://pyotp.readthedocs.io/en/latest/>`_
100
108
  * `Package distribution (PyPI) <https://pypi.python.org/pypi/pyotp>`_
101
109
  * `Change log <https://github.com/pyotp/pyotp/blob/master/Changes.rst>`_
102
110
  * `RFC 4226: HOTP: An HMAC-Based One-Time Password <https://tools.ietf.org/html/rfc4226>`_
103
111
  * `RFC 6238: TOTP: Time-Based One-Time Password Algorithm <https://tools.ietf.org/html/rfc6238>`_
104
112
  * `ROTP <https://github.com/mdp/rotp>`_ - Original Ruby OTP library by `Mark Percival <https://github.com/mdp>`_
105
113
  * `OTPHP <https://github.com/lelag/otphp>`_ - PHP port of ROTP by `Le Lag <https://github.com/lelag>`_
114
+ * `OWASP Authentication Cheat Sheet <https://www.owasp.org/index.php/Authentication_Cheat_Sheet>`_
115
+ * `NIST SP 800-63-3: Digital Authentication Guideline <https://pages.nist.gov/800-63-3/>`_
106
116
 
107
117
  .. image:: https://img.shields.io/travis/pyotp/pyotp.svg
108
118
  :target: https://travis-ci.org/pyotp/pyotp
@@ -113,7 +123,7 @@ Description: PyOTP - The Python One-Time Password Library
113
123
  .. image:: https://img.shields.io/pypi/l/pyotp.svg
114
124
  :target: https://pypi.python.org/pypi/pyotp
115
125
  .. image:: https://readthedocs.org/projects/pyotp/badge/?version=latest
116
- :target: https://pyotp.readthedocs.org/
126
+ :target: https://pyotp.readthedocs.io/
117
127
 
118
128
  Platform: MacOS X
119
129
  Platform: Posix
@@ -3,12 +3,9 @@
3
3
 
4
4
  from __future__ import absolute_import, division, print_function, unicode_literals
5
5
 
6
- import base64
7
- import datetime
8
- import hashlib
9
- import os
10
- import sys
11
- import unittest
6
+ import base64, datetime, hashlib, os, sys, unittest
7
+ from warnings import warn
8
+
12
9
  try:
13
10
  from urllib.parse import urlparse, parse_qsl
14
11
  except ImportError:
@@ -33,6 +30,11 @@ class HOTPExampleValuesFromTheRFC(unittest.TestCase):
33
30
  self.assertEqual(hotp.at(8), '399871')
34
31
  self.assertEqual(hotp.at(9), '520489')
35
32
 
33
+ def test_invalid_input(self):
34
+ hotp = pyotp.HOTP('GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ')
35
+ with self.assertRaises(ValueError):
36
+ hotp.at(-1)
37
+
36
38
  def test_verify_otp_reuse(self):
37
39
  hotp = pyotp.HOTP('GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ')
38
40
  self.assertTrue(hotp.verify('520489', 9))
@@ -65,7 +67,7 @@ class HOTPExampleValuesFromTheRFC(unittest.TestCase):
65
67
  self.assertEqual(url.path, '/FooCorp%21:mark%40percival')
66
68
  self.assertEqual(dict(parse_qsl(url.query)),
67
69
  {'secret': 'wrn3pqx5uqxqvnqr', 'counter': '0',
68
- 'issuer': 'FooCorp!'})
70
+ 'issuer': 'FooCorp%21'})
69
71
 
70
72
  key = 'c7uxuqhgflpw7oruedmglbrk7u6242vb'
71
73
  hotp = pyotp.HOTP(key, digits=8, digest=hashlib.sha256)
@@ -81,14 +83,14 @@ class HOTPExampleValuesFromTheRFC(unittest.TestCase):
81
83
 
82
84
  hotp = pyotp.HOTP(key, digits=8)
83
85
  url = urlparse(
84
- hotp.provisioning_uri('baco@peperina', issuer_name='FooCorp',
86
+ hotp.provisioning_uri('baco@peperina', issuer_name='Foo Corp',
85
87
  initial_count=10))
86
88
  self.assertEqual(url.scheme, 'otpauth')
87
89
  self.assertEqual(url.netloc, 'hotp')
88
- self.assertEqual(url.path, '/FooCorp:baco%40peperina')
90
+ self.assertEqual(url.path, '/Foo%20Corp:baco%40peperina')
89
91
  self.assertEqual(dict(parse_qsl(url.query)),
90
92
  {'secret': 'c7uxuqhgflpw7oruedmglbrk7u6242vb',
91
- 'counter': '10', 'issuer': 'FooCorp',
93
+ 'counter': '10', 'issuer': 'Foo%20Corp',
92
94
  'digits': '8'})
93
95
 
94
96
  def test_other_secret(self):
@@ -137,9 +139,9 @@ class TOTPExampleValuesFromTheRFC(unittest.TestCase):
137
139
  for digest, secret in self.RFC_VALUES:
138
140
  totp = pyotp.TOTP(base64.b32encode(secret), 8, digest)
139
141
  for utime, code in self.RFC_VALUES[(digest, secret)]:
140
- # 32-bit platforms use native functions to handle timestamps, so they fail this test
141
- # (and will fail after 19 January 2038)
142
142
  if utime > sys.maxsize:
143
+ warn("32-bit platforms use native functions to handle timestamps, so they fail this test" +
144
+ " (and will fail after 19 January 2038)")
143
145
  continue
144
146
  value = totp.at(utime)
145
147
  msg = "%s != %s (%s, time=%d)"
@@ -165,6 +167,14 @@ class TOTPExampleValuesFromTheRFC(unittest.TestCase):
165
167
  with Timecop(1297553958 + 30):
166
168
  self.assertFalse(totp.verify('102705'))
167
169
 
170
+ def test_input_before_epoch(self):
171
+ totp = pyotp.TOTP('GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ')
172
+ # -1 and -29.5 round down to 0 (epoch)
173
+ self.assertEqual(totp.at(-1), '755224')
174
+ self.assertEqual(totp.at(-29.5), '755224')
175
+ with self.assertRaises(ValueError):
176
+ totp.at(-30)
177
+
168
178
  def test_validate_totp_with_digit_length(self):
169
179
  totp = pyotp.TOTP('GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ')
170
180
  with Timecop(1111111111):
@@ -189,7 +199,7 @@ class TOTPExampleValuesFromTheRFC(unittest.TestCase):
189
199
  self.assertEqual(url.path, '/FooCorp%21:mark%40percival')
190
200
  self.assertEqual(dict(parse_qsl(url.query)),
191
201
  {'secret': 'wrn3pqx5uqxqvnqr',
192
- 'issuer': 'FooCorp!'})
202
+ 'issuer': 'FooCorp%21'})
193
203
 
194
204
  key = 'c7uxuqhgflpw7oruedmglbrk7u6242vb'
195
205
  totp = pyotp.TOTP(key, digits=8, interval=60, digest=hashlib.sha256)
pyotp-2.2.1/setup.py DELETED
@@ -1,34 +0,0 @@
1
- #!/usr/bin/env python
2
-
3
- import os
4
- from setuptools import setup
5
-
6
- install_requires = [line.rstrip() for line in open(os.path.join(os.path.dirname(__file__), "requirements.txt"))]
7
-
8
- setup(
9
- name='pyotp',
10
- version='2.2.1',
11
- url='https://github.com/pyotp/pyotp',
12
- license='BSD License',
13
- author='PyOTP contributors',
14
- author_email='kislyuk@gmail.com',
15
- description='Python One Time Password Library',
16
- long_description=open('README.rst').read(),
17
- install_requires=install_requires,
18
- packages=['pyotp'],
19
- package_dir={'': 'src'},
20
- platforms=['MacOS X', 'Posix'],
21
- zip_safe=False,
22
- test_suite="test",
23
- classifiers=[
24
- 'Intended Audience :: Developers',
25
- 'License :: OSI Approved :: BSD License',
26
- 'Operating System :: MacOS :: MacOS X',
27
- 'Operating System :: POSIX',
28
- 'Programming Language :: Python',
29
- 'Programming Language :: Python :: 2.7',
30
- 'Programming Language :: Python :: 3.3',
31
- 'Programming Language :: Python :: 3.4',
32
- 'Topic :: Software Development :: Libraries :: Python Modules'
33
- ]
34
- )
@@ -1,42 +0,0 @@
1
- from __future__ import absolute_import, division, print_function, unicode_literals
2
-
3
- from . import utils
4
- from .otp import OTP
5
- from .compat import str
6
-
7
- class HOTP(OTP):
8
- def at(self, count):
9
- """
10
- Generates the OTP for the given count
11
- @param [Integer] count counter
12
- @returns [Integer] OTP
13
- """
14
- return self.generate_otp(count)
15
-
16
- def verify(self, otp, counter):
17
- """
18
- Verifies the OTP passed in against the current time OTP
19
- @param [String/Integer] otp the OTP to check against
20
- @param [Integer] counter the counter of the OTP
21
- """
22
- return utils.strings_equal(str(otp), str(self.at(counter)))
23
-
24
- def provisioning_uri(self, name, initial_count=0, issuer_name=None):
25
- """
26
- Returns the provisioning URI for the OTP
27
- This can then be encoded in a QR Code and used
28
- to provision the Google Authenticator app
29
- @param [String] name of the account
30
- @param [Integer] initial_count starting counter value, defaults to 0
31
- @param [String] the name of the OTP issuer; this will be the
32
- organization title of the OTP entry in Authenticator
33
- @return [String] provisioning uri
34
- """
35
- return utils.build_uri(
36
- self.secret,
37
- name,
38
- initial_count=initial_count,
39
- issuer_name=issuer_name,
40
- algorithm=self.digest().name,
41
- digits=self.digits
42
- )
File without changes
File without changes
File without changes
File without changes
File without changes