PyOTP 2.8.0__tar.gz → 2.9.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.
- {pyotp-2.8.0/src/pyotp.egg-info → pyotp-2.9.0}/PKG-INFO +38 -14
- {pyotp-2.8.0 → pyotp-2.9.0}/README.rst +31 -12
- {pyotp-2.8.0 → pyotp-2.9.0}/pyproject.toml +4 -0
- pyotp-2.9.0/setup.cfg +4 -0
- {pyotp-2.8.0 → pyotp-2.9.0}/setup.py +11 -2
- {pyotp-2.8.0 → pyotp-2.9.0}/src/pyotp/__init__.py +15 -3
- {pyotp-2.8.0 → pyotp-2.9.0}/src/pyotp/contrib/steam.py +8 -1
- {pyotp-2.8.0 → pyotp-2.9.0/src/pyotp.egg-info}/PKG-INFO +38 -14
- {pyotp-2.8.0 → pyotp-2.9.0}/src/pyotp.egg-info/SOURCES.txt +1 -1
- pyotp-2.9.0/src/pyotp.egg-info/requires.txt +6 -0
- {pyotp-2.8.0 → pyotp-2.9.0}/test.py +21 -0
- pyotp-2.8.0/setup.cfg +0 -8
- {pyotp-2.8.0 → pyotp-2.9.0}/LICENSE +0 -0
- {pyotp-2.8.0 → pyotp-2.9.0}/MANIFEST.in +0 -0
- {pyotp-2.8.0 → pyotp-2.9.0}/src/pyotp/compat.py +0 -0
- {pyotp-2.8.0 → pyotp-2.9.0}/src/pyotp/contrib/__init__.py +0 -0
- {pyotp-2.8.0 → pyotp-2.9.0}/src/pyotp/hotp.py +0 -0
- {pyotp-2.8.0 → pyotp-2.9.0}/src/pyotp/otp.py +0 -0
- {pyotp-2.8.0 → pyotp-2.9.0}/src/pyotp/py.typed +0 -0
- {pyotp-2.8.0 → pyotp-2.9.0}/src/pyotp/totp.py +0 -0
- {pyotp-2.8.0 → pyotp-2.9.0}/src/pyotp/utils.py +0 -0
- {pyotp-2.8.0 → pyotp-2.9.0}/src/pyotp.egg-info/dependency_links.txt +0 -0
- {pyotp-2.8.0 → pyotp-2.9.0}/src/pyotp.egg-info/not-zip-safe +0 -0
- {pyotp-2.8.0 → pyotp-2.9.0}/src/pyotp.egg-info/top_level.txt +0 -0
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyotp
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.9.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
|
Author-email: kislyuk@gmail.com
|
|
8
8
|
License: MIT License
|
|
9
|
+
Project-URL: Documentation, https://pyauth.github.io/pyotp
|
|
10
|
+
Project-URL: Source Code, https://github.com/pyauth/pyotp
|
|
11
|
+
Project-URL: Issue Tracker, https://github.com/pyauth/pyotp/issues
|
|
12
|
+
Project-URL: Change Log, https://github.com/pyauth/pyotp/blob/master/Changes.rst
|
|
9
13
|
Platform: MacOS X
|
|
10
14
|
Platform: Posix
|
|
11
15
|
Classifier: Intended Audience :: Developers
|
|
@@ -14,13 +18,14 @@ Classifier: Operating System :: MacOS :: MacOS X
|
|
|
14
18
|
Classifier: Operating System :: POSIX
|
|
15
19
|
Classifier: Programming Language :: Python
|
|
16
20
|
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
18
21
|
Classifier: Programming Language :: Python :: 3.7
|
|
19
22
|
Classifier: Programming Language :: Python :: 3.8
|
|
20
23
|
Classifier: Programming Language :: Python :: 3.9
|
|
21
24
|
Classifier: Programming Language :: Python :: 3.10
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
26
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
27
|
Requires-Python: >=3.7
|
|
28
|
+
Provides-Extra: test
|
|
24
29
|
License-File: LICENSE
|
|
25
30
|
|
|
26
31
|
PyOTP - The Python One-Time Password Library
|
|
@@ -43,19 +48,21 @@ minimum, application implementers should follow this checklist:
|
|
|
43
48
|
|
|
44
49
|
- Ensure transport confidentiality by using HTTPS
|
|
45
50
|
- Ensure HOTP/TOTP secret confidentiality by storing secrets in a controlled access database
|
|
46
|
-
- Deny replay attacks by rejecting one-time passwords that have been used by the client (this requires storing the most
|
|
47
|
-
recently authenticated timestamp, OTP, or hash of the OTP in your database, and rejecting the OTP when a match is
|
|
51
|
+
- Deny replay attacks by rejecting one-time passwords that have been used by the client (this requires storing the most
|
|
52
|
+
recently authenticated timestamp, OTP, or hash of the OTP in your database, and rejecting the OTP when a match is
|
|
53
|
+
seen)
|
|
48
54
|
- Throttle (rate limit) brute-force attacks against your application's login functionality (see RFC 4226, section 7.3)
|
|
49
55
|
- When implementing a "greenfield" application, consider supporting
|
|
50
56
|
`FIDO U2F <https://en.wikipedia.org/wiki/Universal_2nd_Factor>`_/`WebAuthn <https://www.w3.org/TR/webauthn/>`_ in
|
|
51
57
|
addition to HOTP/TOTP. U2F uses asymmetric cryptography to avoid using a shared secret design, which strengthens your
|
|
52
58
|
MFA solution against server-side attacks. Hardware U2F also sequesters the client secret in a dedicated single-purpose
|
|
53
59
|
device, which strengthens your clients against client-side attacks. And by automating scoping of credentials to
|
|
54
|
-
relying party IDs (application origin/domain names), U2F adds protection against phishing attacks. One implementation
|
|
55
|
-
FIDO U2F/WebAuthn is PyOTP's sister project, `PyWARP <https://github.com/pyauth/pywarp>`_.
|
|
60
|
+
relying party IDs (application origin/domain names), U2F adds protection against phishing attacks. One implementation
|
|
61
|
+
of FIDO U2F/WebAuthn is PyOTP's sister project, `PyWARP <https://github.com/pyauth/pywarp>`_.
|
|
56
62
|
|
|
57
63
|
We also recommend that implementers read the
|
|
58
|
-
`OWASP Authentication Cheat Sheet
|
|
64
|
+
`OWASP Authentication Cheat Sheet
|
|
65
|
+
<https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md>`_ and
|
|
59
66
|
`NIST SP 800-63-3: Digital Authentication Guideline <https://pages.nist.gov/800-63-3/>`_ for a high level overview of
|
|
60
67
|
authentication best practices.
|
|
61
68
|
|
|
@@ -64,8 +71,10 @@ Quick overview of using One Time Passwords on your phone
|
|
|
64
71
|
|
|
65
72
|
* OTPs involve a shared secret, stored both on the phone and the server
|
|
66
73
|
* OTPs can be generated on a phone without internet connectivity
|
|
67
|
-
* OTPs should always be used as a second factor of authentication (if your phone is lost, you account is still secured
|
|
68
|
-
|
|
74
|
+
* OTPs should always be used as a second factor of authentication (if your phone is lost, you account is still secured
|
|
75
|
+
with a password)
|
|
76
|
+
* Google Authenticator and other OTP client apps allow you to store multiple OTP secrets and provision those using a QR
|
|
77
|
+
Code
|
|
69
78
|
|
|
70
79
|
Installation
|
|
71
80
|
------------
|
|
@@ -108,7 +117,8 @@ Counter-based OTPs
|
|
|
108
117
|
|
|
109
118
|
Generating a Secret Key
|
|
110
119
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
111
|
-
A helper function is provided to generate a 32-character base32 secret, compatible with Google Authenticator and other
|
|
120
|
+
A helper function is provided to generate a 32-character base32 secret, compatible with Google Authenticator and other
|
|
121
|
+
OTP apps::
|
|
112
122
|
|
|
113
123
|
pyotp.random_base32()
|
|
114
124
|
|
|
@@ -119,8 +129,8 @@ Some applications want the secret key to be formatted as a hex-encoded string::
|
|
|
119
129
|
Google Authenticator Compatible
|
|
120
130
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
121
131
|
|
|
122
|
-
PyOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. PyOTP includes
|
|
123
|
-
ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps::
|
|
132
|
+
PyOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. PyOTP includes
|
|
133
|
+
the ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps::
|
|
124
134
|
|
|
125
135
|
pyotp.totp.TOTP('JBSWY3DPEHPK3PXP').provisioning_uri(name='alice@google.com', issuer_name='Secure App')
|
|
126
136
|
|
|
@@ -130,8 +140,8 @@ ability to generate provisioning URIs for use with the QR Code scanner built int
|
|
|
130
140
|
|
|
131
141
|
>>> 'otpauth://hotp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure%20App&counter=0'
|
|
132
142
|
|
|
133
|
-
This URL can then be rendered as a QR Code (for example, using https://github.com/soldair/node-qrcode) which can then be
|
|
134
|
-
and added to the users list of OTP credentials.
|
|
143
|
+
This URL can then be rendered as a QR Code (for example, using https://github.com/soldair/node-qrcode) which can then be
|
|
144
|
+
scanned and added to the users list of OTP credentials.
|
|
135
145
|
|
|
136
146
|
Parsing these URLs is also supported::
|
|
137
147
|
|
|
@@ -156,6 +166,13 @@ Now run the following and compare the output::
|
|
|
156
166
|
totp = pyotp.TOTP("JBSWY3DPEHPK3PXP")
|
|
157
167
|
print("Current OTP:", totp.now())
|
|
158
168
|
|
|
169
|
+
Third-party contributions
|
|
170
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
171
|
+
The following third-party contributions are not described by a standard, not officially supported, and provided for
|
|
172
|
+
reference only:
|
|
173
|
+
|
|
174
|
+
* ``pyotp.contrib.Steam()``: An implementation of Steam TOTP. Uses the same API as `pyotp.TOTP()`.
|
|
175
|
+
|
|
159
176
|
Links
|
|
160
177
|
~~~~~
|
|
161
178
|
|
|
@@ -175,6 +192,13 @@ For new applications:
|
|
|
175
192
|
* `WebAuthn <https://www.w3.org/TR/webauthn/>`_
|
|
176
193
|
* `PyWARP <https://github.com/pyauth/pywarp>`_
|
|
177
194
|
|
|
195
|
+
Versioning
|
|
196
|
+
~~~~~~~~~~
|
|
197
|
+
This package follows the `Semantic Versioning 2.0.0 <http://semver.org/>`_ standard. To control changes, it is
|
|
198
|
+
recommended that application developers pin the package version and manage it using `pip-tools
|
|
199
|
+
<https://github.com/jazzband/pip-tools>`_ or similar. For library developers, pinning the major version is
|
|
200
|
+
recommended.
|
|
201
|
+
|
|
178
202
|
.. image:: https://github.com/pyauth/pyotp/workflows/Python%20package/badge.svg
|
|
179
203
|
:target: https://github.com/pyauth/pyotp/actions
|
|
180
204
|
.. image:: https://img.shields.io/codecov/c/github/pyauth/pyotp/master.svg
|
|
@@ -18,19 +18,21 @@ minimum, application implementers should follow this checklist:
|
|
|
18
18
|
|
|
19
19
|
- Ensure transport confidentiality by using HTTPS
|
|
20
20
|
- Ensure HOTP/TOTP secret confidentiality by storing secrets in a controlled access database
|
|
21
|
-
- Deny replay attacks by rejecting one-time passwords that have been used by the client (this requires storing the most
|
|
22
|
-
recently authenticated timestamp, OTP, or hash of the OTP in your database, and rejecting the OTP when a match is
|
|
21
|
+
- Deny replay attacks by rejecting one-time passwords that have been used by the client (this requires storing the most
|
|
22
|
+
recently authenticated timestamp, OTP, or hash of the OTP in your database, and rejecting the OTP when a match is
|
|
23
|
+
seen)
|
|
23
24
|
- Throttle (rate limit) brute-force attacks against your application's login functionality (see RFC 4226, section 7.3)
|
|
24
25
|
- When implementing a "greenfield" application, consider supporting
|
|
25
26
|
`FIDO U2F <https://en.wikipedia.org/wiki/Universal_2nd_Factor>`_/`WebAuthn <https://www.w3.org/TR/webauthn/>`_ in
|
|
26
27
|
addition to HOTP/TOTP. U2F uses asymmetric cryptography to avoid using a shared secret design, which strengthens your
|
|
27
28
|
MFA solution against server-side attacks. Hardware U2F also sequesters the client secret in a dedicated single-purpose
|
|
28
29
|
device, which strengthens your clients against client-side attacks. And by automating scoping of credentials to
|
|
29
|
-
relying party IDs (application origin/domain names), U2F adds protection against phishing attacks. One implementation
|
|
30
|
-
FIDO U2F/WebAuthn is PyOTP's sister project, `PyWARP <https://github.com/pyauth/pywarp>`_.
|
|
30
|
+
relying party IDs (application origin/domain names), U2F adds protection against phishing attacks. One implementation
|
|
31
|
+
of FIDO U2F/WebAuthn is PyOTP's sister project, `PyWARP <https://github.com/pyauth/pywarp>`_.
|
|
31
32
|
|
|
32
33
|
We also recommend that implementers read the
|
|
33
|
-
`OWASP Authentication Cheat Sheet
|
|
34
|
+
`OWASP Authentication Cheat Sheet
|
|
35
|
+
<https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md>`_ and
|
|
34
36
|
`NIST SP 800-63-3: Digital Authentication Guideline <https://pages.nist.gov/800-63-3/>`_ for a high level overview of
|
|
35
37
|
authentication best practices.
|
|
36
38
|
|
|
@@ -39,8 +41,10 @@ Quick overview of using One Time Passwords on your phone
|
|
|
39
41
|
|
|
40
42
|
* OTPs involve a shared secret, stored both on the phone and the server
|
|
41
43
|
* OTPs can be generated on a phone without internet connectivity
|
|
42
|
-
* OTPs should always be used as a second factor of authentication (if your phone is lost, you account is still secured
|
|
43
|
-
|
|
44
|
+
* OTPs should always be used as a second factor of authentication (if your phone is lost, you account is still secured
|
|
45
|
+
with a password)
|
|
46
|
+
* Google Authenticator and other OTP client apps allow you to store multiple OTP secrets and provision those using a QR
|
|
47
|
+
Code
|
|
44
48
|
|
|
45
49
|
Installation
|
|
46
50
|
------------
|
|
@@ -83,7 +87,8 @@ Counter-based OTPs
|
|
|
83
87
|
|
|
84
88
|
Generating a Secret Key
|
|
85
89
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
86
|
-
A helper function is provided to generate a 32-character base32 secret, compatible with Google Authenticator and other
|
|
90
|
+
A helper function is provided to generate a 32-character base32 secret, compatible with Google Authenticator and other
|
|
91
|
+
OTP apps::
|
|
87
92
|
|
|
88
93
|
pyotp.random_base32()
|
|
89
94
|
|
|
@@ -94,8 +99,8 @@ Some applications want the secret key to be formatted as a hex-encoded string::
|
|
|
94
99
|
Google Authenticator Compatible
|
|
95
100
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
96
101
|
|
|
97
|
-
PyOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. PyOTP includes
|
|
98
|
-
ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps::
|
|
102
|
+
PyOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. PyOTP includes
|
|
103
|
+
the ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps::
|
|
99
104
|
|
|
100
105
|
pyotp.totp.TOTP('JBSWY3DPEHPK3PXP').provisioning_uri(name='alice@google.com', issuer_name='Secure App')
|
|
101
106
|
|
|
@@ -105,8 +110,8 @@ ability to generate provisioning URIs for use with the QR Code scanner built int
|
|
|
105
110
|
|
|
106
111
|
>>> 'otpauth://hotp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure%20App&counter=0'
|
|
107
112
|
|
|
108
|
-
This URL can then be rendered as a QR Code (for example, using https://github.com/soldair/node-qrcode) which can then be
|
|
109
|
-
and added to the users list of OTP credentials.
|
|
113
|
+
This URL can then be rendered as a QR Code (for example, using https://github.com/soldair/node-qrcode) which can then be
|
|
114
|
+
scanned and added to the users list of OTP credentials.
|
|
110
115
|
|
|
111
116
|
Parsing these URLs is also supported::
|
|
112
117
|
|
|
@@ -131,6 +136,13 @@ Now run the following and compare the output::
|
|
|
131
136
|
totp = pyotp.TOTP("JBSWY3DPEHPK3PXP")
|
|
132
137
|
print("Current OTP:", totp.now())
|
|
133
138
|
|
|
139
|
+
Third-party contributions
|
|
140
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
141
|
+
The following third-party contributions are not described by a standard, not officially supported, and provided for
|
|
142
|
+
reference only:
|
|
143
|
+
|
|
144
|
+
* ``pyotp.contrib.Steam()``: An implementation of Steam TOTP. Uses the same API as `pyotp.TOTP()`.
|
|
145
|
+
|
|
134
146
|
Links
|
|
135
147
|
~~~~~
|
|
136
148
|
|
|
@@ -150,6 +162,13 @@ For new applications:
|
|
|
150
162
|
* `WebAuthn <https://www.w3.org/TR/webauthn/>`_
|
|
151
163
|
* `PyWARP <https://github.com/pyauth/pywarp>`_
|
|
152
164
|
|
|
165
|
+
Versioning
|
|
166
|
+
~~~~~~~~~~
|
|
167
|
+
This package follows the `Semantic Versioning 2.0.0 <http://semver.org/>`_ standard. To control changes, it is
|
|
168
|
+
recommended that application developers pin the package version and manage it using `pip-tools
|
|
169
|
+
<https://github.com/jazzband/pip-tools>`_ or similar. For library developers, pinning the major version is
|
|
170
|
+
recommended.
|
|
171
|
+
|
|
153
172
|
.. image:: https://github.com/pyauth/pyotp/workflows/Python%20package/badge.svg
|
|
154
173
|
:target: https://github.com/pyauth/pyotp/actions
|
|
155
174
|
.. image:: https://img.shields.io/codecov/c/github/pyauth/pyotp/master.svg
|
pyotp-2.9.0/setup.cfg
ADDED
|
@@ -4,8 +4,14 @@ from setuptools import setup
|
|
|
4
4
|
|
|
5
5
|
setup(
|
|
6
6
|
name="pyotp",
|
|
7
|
-
version="2.
|
|
7
|
+
version="2.9.0",
|
|
8
8
|
url="https://github.com/pyotp/pyotp",
|
|
9
|
+
project_urls={
|
|
10
|
+
"Documentation": "https://pyauth.github.io/pyotp",
|
|
11
|
+
"Source Code": "https://github.com/pyauth/pyotp",
|
|
12
|
+
"Issue Tracker": "https://github.com/pyauth/pyotp/issues",
|
|
13
|
+
"Change Log": "https://github.com/pyauth/pyotp/blob/master/Changes.rst",
|
|
14
|
+
},
|
|
9
15
|
license="MIT License",
|
|
10
16
|
author="PyOTP contributors",
|
|
11
17
|
author_email="kislyuk@gmail.com",
|
|
@@ -13,6 +19,9 @@ setup(
|
|
|
13
19
|
long_description=open("README.rst").read(),
|
|
14
20
|
python_requires=">=3.7",
|
|
15
21
|
install_requires=[],
|
|
22
|
+
extras_require={
|
|
23
|
+
"test": ["coverage", "wheel", "ruff", "mypy"],
|
|
24
|
+
},
|
|
16
25
|
packages=["pyotp", "pyotp.contrib"],
|
|
17
26
|
package_dir={"": "src"},
|
|
18
27
|
package_data={"pyotp": ["py.typed"]},
|
|
@@ -26,11 +35,11 @@ setup(
|
|
|
26
35
|
"Operating System :: POSIX",
|
|
27
36
|
"Programming Language :: Python",
|
|
28
37
|
"Programming Language :: Python :: 3",
|
|
29
|
-
"Programming Language :: Python :: 3.6",
|
|
30
38
|
"Programming Language :: Python :: 3.7",
|
|
31
39
|
"Programming Language :: Python :: 3.8",
|
|
32
40
|
"Programming Language :: Python :: 3.9",
|
|
33
41
|
"Programming Language :: Python :: 3.10",
|
|
42
|
+
"Programming Language :: Python :: 3.11",
|
|
34
43
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
35
44
|
],
|
|
36
45
|
)
|
|
@@ -40,6 +40,12 @@ def parse_uri(uri: str) -> OTP:
|
|
|
40
40
|
# Secret (to be filled in later)
|
|
41
41
|
secret = None
|
|
42
42
|
|
|
43
|
+
# Encoder (to be filled in later)
|
|
44
|
+
encoder = None
|
|
45
|
+
|
|
46
|
+
# Digits (to be filled in later)
|
|
47
|
+
digits = None
|
|
48
|
+
|
|
43
49
|
# Data we'll parse to the correct constructor
|
|
44
50
|
otp_data: Dict[str, Any] = {}
|
|
45
51
|
|
|
@@ -74,10 +80,10 @@ def parse_uri(uri: str) -> OTP:
|
|
|
74
80
|
otp_data["digest"] = hashlib.sha512
|
|
75
81
|
else:
|
|
76
82
|
raise ValueError("Invalid value for algorithm, must be SHA1, SHA256 or SHA512")
|
|
83
|
+
elif key == "encoder":
|
|
84
|
+
encoder = value
|
|
77
85
|
elif key == "digits":
|
|
78
86
|
digits = int(value)
|
|
79
|
-
if digits not in [6, 7, 8]:
|
|
80
|
-
raise ValueError("Digits may only be 6, 7, or 8")
|
|
81
87
|
otp_data["digits"] = digits
|
|
82
88
|
elif key == "period":
|
|
83
89
|
otp_data["interval"] = int(value)
|
|
@@ -85,11 +91,17 @@ def parse_uri(uri: str) -> OTP:
|
|
|
85
91
|
otp_data["initial_count"] = int(value)
|
|
86
92
|
elif key != "image":
|
|
87
93
|
raise ValueError("{} is not a valid parameter".format(key))
|
|
88
|
-
|
|
94
|
+
|
|
95
|
+
if encoder != "steam":
|
|
96
|
+
if digits is not None and digits not in [6, 7, 8]:
|
|
97
|
+
raise ValueError("Digits may only be 6, 7, or 8")
|
|
98
|
+
|
|
89
99
|
if not secret:
|
|
90
100
|
raise ValueError("No secret found in URI")
|
|
91
101
|
|
|
92
102
|
# Create objects
|
|
103
|
+
if encoder == "steam":
|
|
104
|
+
return contrib.Steam(secret, **otp_data)
|
|
93
105
|
if parsed_uri.netloc == "totp":
|
|
94
106
|
return TOTP(secret, **otp_data)
|
|
95
107
|
elif parsed_uri.netloc == "hotp":
|
|
@@ -12,7 +12,14 @@ class Steam(TOTP):
|
|
|
12
12
|
Steam's custom TOTP. Subclass of `pyotp.totp.TOTP`.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
def __init__(
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
s: str,
|
|
18
|
+
name: Optional[str] = None,
|
|
19
|
+
issuer: Optional[str] = None,
|
|
20
|
+
interval: int = 30,
|
|
21
|
+
digits: int = 5
|
|
22
|
+
) -> None:
|
|
16
23
|
"""
|
|
17
24
|
:param s: secret in base32 format
|
|
18
25
|
:param interval: the time interval in seconds for OTP. This defaults to 30.
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyotp
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.9.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
|
Author-email: kislyuk@gmail.com
|
|
8
8
|
License: MIT License
|
|
9
|
+
Project-URL: Documentation, https://pyauth.github.io/pyotp
|
|
10
|
+
Project-URL: Source Code, https://github.com/pyauth/pyotp
|
|
11
|
+
Project-URL: Issue Tracker, https://github.com/pyauth/pyotp/issues
|
|
12
|
+
Project-URL: Change Log, https://github.com/pyauth/pyotp/blob/master/Changes.rst
|
|
9
13
|
Platform: MacOS X
|
|
10
14
|
Platform: Posix
|
|
11
15
|
Classifier: Intended Audience :: Developers
|
|
@@ -14,13 +18,14 @@ Classifier: Operating System :: MacOS :: MacOS X
|
|
|
14
18
|
Classifier: Operating System :: POSIX
|
|
15
19
|
Classifier: Programming Language :: Python
|
|
16
20
|
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
18
21
|
Classifier: Programming Language :: Python :: 3.7
|
|
19
22
|
Classifier: Programming Language :: Python :: 3.8
|
|
20
23
|
Classifier: Programming Language :: Python :: 3.9
|
|
21
24
|
Classifier: Programming Language :: Python :: 3.10
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
26
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
27
|
Requires-Python: >=3.7
|
|
28
|
+
Provides-Extra: test
|
|
24
29
|
License-File: LICENSE
|
|
25
30
|
|
|
26
31
|
PyOTP - The Python One-Time Password Library
|
|
@@ -43,19 +48,21 @@ minimum, application implementers should follow this checklist:
|
|
|
43
48
|
|
|
44
49
|
- Ensure transport confidentiality by using HTTPS
|
|
45
50
|
- Ensure HOTP/TOTP secret confidentiality by storing secrets in a controlled access database
|
|
46
|
-
- Deny replay attacks by rejecting one-time passwords that have been used by the client (this requires storing the most
|
|
47
|
-
recently authenticated timestamp, OTP, or hash of the OTP in your database, and rejecting the OTP when a match is
|
|
51
|
+
- Deny replay attacks by rejecting one-time passwords that have been used by the client (this requires storing the most
|
|
52
|
+
recently authenticated timestamp, OTP, or hash of the OTP in your database, and rejecting the OTP when a match is
|
|
53
|
+
seen)
|
|
48
54
|
- Throttle (rate limit) brute-force attacks against your application's login functionality (see RFC 4226, section 7.3)
|
|
49
55
|
- When implementing a "greenfield" application, consider supporting
|
|
50
56
|
`FIDO U2F <https://en.wikipedia.org/wiki/Universal_2nd_Factor>`_/`WebAuthn <https://www.w3.org/TR/webauthn/>`_ in
|
|
51
57
|
addition to HOTP/TOTP. U2F uses asymmetric cryptography to avoid using a shared secret design, which strengthens your
|
|
52
58
|
MFA solution against server-side attacks. Hardware U2F also sequesters the client secret in a dedicated single-purpose
|
|
53
59
|
device, which strengthens your clients against client-side attacks. And by automating scoping of credentials to
|
|
54
|
-
relying party IDs (application origin/domain names), U2F adds protection against phishing attacks. One implementation
|
|
55
|
-
FIDO U2F/WebAuthn is PyOTP's sister project, `PyWARP <https://github.com/pyauth/pywarp>`_.
|
|
60
|
+
relying party IDs (application origin/domain names), U2F adds protection against phishing attacks. One implementation
|
|
61
|
+
of FIDO U2F/WebAuthn is PyOTP's sister project, `PyWARP <https://github.com/pyauth/pywarp>`_.
|
|
56
62
|
|
|
57
63
|
We also recommend that implementers read the
|
|
58
|
-
`OWASP Authentication Cheat Sheet
|
|
64
|
+
`OWASP Authentication Cheat Sheet
|
|
65
|
+
<https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md>`_ and
|
|
59
66
|
`NIST SP 800-63-3: Digital Authentication Guideline <https://pages.nist.gov/800-63-3/>`_ for a high level overview of
|
|
60
67
|
authentication best practices.
|
|
61
68
|
|
|
@@ -64,8 +71,10 @@ Quick overview of using One Time Passwords on your phone
|
|
|
64
71
|
|
|
65
72
|
* OTPs involve a shared secret, stored both on the phone and the server
|
|
66
73
|
* OTPs can be generated on a phone without internet connectivity
|
|
67
|
-
* OTPs should always be used as a second factor of authentication (if your phone is lost, you account is still secured
|
|
68
|
-
|
|
74
|
+
* OTPs should always be used as a second factor of authentication (if your phone is lost, you account is still secured
|
|
75
|
+
with a password)
|
|
76
|
+
* Google Authenticator and other OTP client apps allow you to store multiple OTP secrets and provision those using a QR
|
|
77
|
+
Code
|
|
69
78
|
|
|
70
79
|
Installation
|
|
71
80
|
------------
|
|
@@ -108,7 +117,8 @@ Counter-based OTPs
|
|
|
108
117
|
|
|
109
118
|
Generating a Secret Key
|
|
110
119
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
111
|
-
A helper function is provided to generate a 32-character base32 secret, compatible with Google Authenticator and other
|
|
120
|
+
A helper function is provided to generate a 32-character base32 secret, compatible with Google Authenticator and other
|
|
121
|
+
OTP apps::
|
|
112
122
|
|
|
113
123
|
pyotp.random_base32()
|
|
114
124
|
|
|
@@ -119,8 +129,8 @@ Some applications want the secret key to be formatted as a hex-encoded string::
|
|
|
119
129
|
Google Authenticator Compatible
|
|
120
130
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
121
131
|
|
|
122
|
-
PyOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. PyOTP includes
|
|
123
|
-
ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps::
|
|
132
|
+
PyOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. PyOTP includes
|
|
133
|
+
the ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps::
|
|
124
134
|
|
|
125
135
|
pyotp.totp.TOTP('JBSWY3DPEHPK3PXP').provisioning_uri(name='alice@google.com', issuer_name='Secure App')
|
|
126
136
|
|
|
@@ -130,8 +140,8 @@ ability to generate provisioning URIs for use with the QR Code scanner built int
|
|
|
130
140
|
|
|
131
141
|
>>> 'otpauth://hotp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure%20App&counter=0'
|
|
132
142
|
|
|
133
|
-
This URL can then be rendered as a QR Code (for example, using https://github.com/soldair/node-qrcode) which can then be
|
|
134
|
-
and added to the users list of OTP credentials.
|
|
143
|
+
This URL can then be rendered as a QR Code (for example, using https://github.com/soldair/node-qrcode) which can then be
|
|
144
|
+
scanned and added to the users list of OTP credentials.
|
|
135
145
|
|
|
136
146
|
Parsing these URLs is also supported::
|
|
137
147
|
|
|
@@ -156,6 +166,13 @@ Now run the following and compare the output::
|
|
|
156
166
|
totp = pyotp.TOTP("JBSWY3DPEHPK3PXP")
|
|
157
167
|
print("Current OTP:", totp.now())
|
|
158
168
|
|
|
169
|
+
Third-party contributions
|
|
170
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
171
|
+
The following third-party contributions are not described by a standard, not officially supported, and provided for
|
|
172
|
+
reference only:
|
|
173
|
+
|
|
174
|
+
* ``pyotp.contrib.Steam()``: An implementation of Steam TOTP. Uses the same API as `pyotp.TOTP()`.
|
|
175
|
+
|
|
159
176
|
Links
|
|
160
177
|
~~~~~
|
|
161
178
|
|
|
@@ -175,6 +192,13 @@ For new applications:
|
|
|
175
192
|
* `WebAuthn <https://www.w3.org/TR/webauthn/>`_
|
|
176
193
|
* `PyWARP <https://github.com/pyauth/pywarp>`_
|
|
177
194
|
|
|
195
|
+
Versioning
|
|
196
|
+
~~~~~~~~~~
|
|
197
|
+
This package follows the `Semantic Versioning 2.0.0 <http://semver.org/>`_ standard. To control changes, it is
|
|
198
|
+
recommended that application developers pin the package version and manage it using `pip-tools
|
|
199
|
+
<https://github.com/jazzband/pip-tools>`_ or similar. For library developers, pinning the major version is
|
|
200
|
+
recommended.
|
|
201
|
+
|
|
178
202
|
.. image:: https://github.com/pyauth/pyotp/workflows/Python%20package/badge.svg
|
|
179
203
|
:target: https://github.com/pyauth/pyotp/actions
|
|
180
204
|
.. image:: https://img.shields.io/codecov/c/github/pyauth/pyotp/master.svg
|
|
@@ -2,7 +2,6 @@ LICENSE
|
|
|
2
2
|
MANIFEST.in
|
|
3
3
|
README.rst
|
|
4
4
|
pyproject.toml
|
|
5
|
-
setup.cfg
|
|
6
5
|
setup.py
|
|
7
6
|
test.py
|
|
8
7
|
src/pyotp/__init__.py
|
|
@@ -16,6 +15,7 @@ src/pyotp.egg-info/PKG-INFO
|
|
|
16
15
|
src/pyotp.egg-info/SOURCES.txt
|
|
17
16
|
src/pyotp.egg-info/dependency_links.txt
|
|
18
17
|
src/pyotp.egg-info/not-zip-safe
|
|
18
|
+
src/pyotp.egg-info/requires.txt
|
|
19
19
|
src/pyotp.egg-info/top_level.txt
|
|
20
20
|
src/pyotp/contrib/__init__.py
|
|
21
21
|
src/pyotp/contrib/steam.py
|
|
@@ -364,6 +364,13 @@ class ParseUriTest(unittest.TestCase):
|
|
|
364
364
|
with self.assertRaises(ValueError) as cm:
|
|
365
365
|
pyotp.parse_uri("otpauth://totp?algorithm=aes")
|
|
366
366
|
self.assertEqual("Invalid value for algorithm, must be SHA1, SHA256 or SHA512", str(cm.exception))
|
|
367
|
+
|
|
368
|
+
def test_parse_steam(self):
|
|
369
|
+
otp = pyotp.parse_uri("otpauth://totp/Steam:?secret=SOME_SECRET&encoder=steam")
|
|
370
|
+
self.assertEqual(type(otp), pyotp.contrib.Steam)
|
|
371
|
+
|
|
372
|
+
otp = pyotp.parse_uri("otpauth://totp/Steam:?secret=SOME_SECRET")
|
|
373
|
+
self.assertNotEqual(type(otp), pyotp.contrib.Steam)
|
|
367
374
|
|
|
368
375
|
@unittest.skipIf(sys.version_info < (3, 6), "Skipping test that requires deterministic dict key enumeration")
|
|
369
376
|
def test_algorithms(self):
|
|
@@ -420,6 +427,20 @@ class ParseUriTest(unittest.TestCase):
|
|
|
420
427
|
otp = pyotp.parse_uri(otp.provisioning_uri(name="n", issuer_name="i", image="https://test.net/test.png"))
|
|
421
428
|
self.assertEqual(hashlib.sha512, otp.digest)
|
|
422
429
|
|
|
430
|
+
otp = pyotp.parse_uri("otpauth://totp/Steam:?secret=FMXNK4QEGKVPULRTADY6JIDK5VHUBGZW&encoder=steam")
|
|
431
|
+
self.assertEqual(type(otp), pyotp.contrib.Steam)
|
|
432
|
+
self.assertEqual(otp.at(0), "C5V56")
|
|
433
|
+
self.assertEqual(otp.at(30), "QJY8Y")
|
|
434
|
+
self.assertEqual(otp.at(60), "R3WQY")
|
|
435
|
+
self.assertEqual(otp.at(90), "JG3T3")
|
|
436
|
+
|
|
437
|
+
# period and digits should be ignored
|
|
438
|
+
otp = pyotp.parse_uri("otpauth://totp/Steam:?secret=FMXNK4QEGKVPULRTADY6JIDK5VHUBGZW&period=15&digits=7&encoder=steam")
|
|
439
|
+
self.assertEqual(type(otp), pyotp.contrib.Steam)
|
|
440
|
+
self.assertEqual(otp.at(0), "C5V56")
|
|
441
|
+
self.assertEqual(otp.at(30), "QJY8Y")
|
|
442
|
+
self.assertEqual(otp.at(60), "R3WQY")
|
|
443
|
+
self.assertEqual(otp.at(90), "JG3T3")
|
|
423
444
|
|
|
424
445
|
class Timecop(object):
|
|
425
446
|
"""
|
pyotp-2.8.0/setup.cfg
DELETED
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|