[Python-modules-commits] [python-pyotp] 01/03: New upstream version 2.2.6
Hugo Lefeuvre
hle at moszumanska.debian.org
Thu Oct 5 13:28:46 UTC 2017
This is an automated email from the git hooks/post-receive script.
hle pushed a commit to branch master
in repository python-pyotp.
commit ecc31e0e543a0ded4639bd5d81e5841ba223de0c
Author: Hugo Lefeuvre <hle at debian.org>
Date: Thu Oct 5 15:18:04 2017 +0200
New upstream version 2.2.6
---
PKG-INFO | 36 ++++++++++++++++++-----------
README.rst | 34 ++++++++++++++++++----------
setup.cfg | 3 +--
setup.py | 40 ++++++++++++++++-----------------
src/pyotp.egg-info/PKG-INFO | 36 ++++++++++++++++++-----------
src/pyotp/hotp.py | 42 +++++++++++++++++++++++-----------
src/pyotp/otp.py | 25 ++++++++++++---------
src/pyotp/totp.py | 55 ++++++++++++++++++++++++++++++++-------------
src/pyotp/utils.py | 28 ++++++++++++++---------
test.py | 32 +++++++++++++++++---------
10 files changed, 210 insertions(+), 121 deletions(-)
diff --git a/PKG-INFO b/PKG-INFO
index 1a62fa1..7a8312d 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: pyotp
-Version: 2.2.1
+Version: 2.2.6
Summary: Python One Time Password Library
Home-page: https://github.com/pyotp/pyotp
Author: PyOTP contributors
@@ -20,13 +20,15 @@ Description: PyOTP - The Python One-Time Password Library
compatible app. Users can set up auth tokens in their apps easily by using their phone camera to scan `otpauth://
<https://github.com/google/google-authenticator/wiki/Key-Uri-Format>`_ QR codes provided by PyOTP.
+ 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.
+
Quick overview of using One Time Passwords on your phone
--------------------------------------------------------
* OTPs involve a shared secret, stored both on the phone and the server
* OTPs can be generated on a phone without internet connectivity
- * OTPs should always be used as a second factor of authentication(if your phone is lost, you account is still secured with a password)
- * Google Authenticator allows you to store multiple OTP secrets and provision those using a QR Code(no more typing in the secret)
+ * OTPs should always be used as a second factor of authentication (if your phone is lost, you account is still secured with a password)
+ * Google Authenticator and other OTP client apps allow you to store multiple OTP secrets and provision those using a QR Code
Installation
------------
@@ -37,7 +39,7 @@ Description: PyOTP - The Python One-Time Password Library
Usage
-----
- Time based OTPs
+ Time-based OTPs
~~~~~~~~~~~~~~~
::
@@ -66,23 +68,29 @@ Description: PyOTP - The Python One-Time Password Library
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
- pyotp.random_base32() # returns a 16 character base32 secret. Compatible with Google Authenticator
+ pyotp.random_base32() # returns a 16 character base32 secret. Compatible with Google Authenticator and other OTP apps
Google Authenticator Compatible
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- The library works with the Google Authenticator iPhone and Android app, and also includes the ability to generate
- provisioning URI's for use with the QR Code scanner built into the app::
+ PyOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. PyOTP includes the
+ ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps::
+
+ pyotp.totp.TOTP('JBSWY3DPEHPK3PXP').provisioning_uri("alice at google.com", issuer_name="Secure App")
+
+ >>> 'otpauth://totp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure%20App'
+
+ pyotp.hotp.HOTP('JBSWY3DPEHPK3PXP').provisioning_uri("alice at google.com", initial_count=0, issuer_name="Secure App")
- totp.provisioning_uri("alice at google.com") # => 'otpauth://totp/alice@google.com?secret=JBSWY3DPEHPK3PXP'
- hotp.provisioning_uri("alice at google.com", 0) # => 'otpauth://hotp/alice@google.com?secret=JBSWY3DPEHPK3PXP&counter=0'
+ >>> 'otpauth://hotp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure%20App&counter=0'
- This can then be rendered as a QR Code which can then be scanned and added to the users list of OTP credentials.
+ This URL can then be rendered as a QR Code (for example, using https://github.com/neocotic/qrious) which can then be scanned
+ and added to the users list of OTP credentials.
Working example
~~~~~~~~~~~~~~~
- Scan the following barcode with your phone, using Google Authenticator
+ Scan the following barcode with your phone's OTP app (e.g. Google Authenticator):
.. image:: http://chart.apis.google.com/chart?cht=qr&chs=250x250&chl=otpauth%3A%2F%2Ftotp%2Falice%40google.com%3Fsecret%3DJBSWY3DPEHPK3PXP
@@ -96,13 +104,15 @@ Description: PyOTP - The Python One-Time Password Library
~~~~~
* `Project home page (GitHub) <https://github.com/pyotp/pyotp>`_
- * `Documentation (Read the Docs) <https://pyotp.readthedocs.org/en/latest/>`_
+ * `Documentation (Read the Docs) <https://pyotp.readthedocs.io/en/latest/>`_
* `Package distribution (PyPI) <https://pypi.python.org/pypi/pyotp>`_
* `Change log <https://github.com/pyotp/pyotp/blob/master/Changes.rst>`_
* `RFC 4226: HOTP: An HMAC-Based One-Time Password <https://tools.ietf.org/html/rfc4226>`_
* `RFC 6238: TOTP: Time-Based One-Time Password Algorithm <https://tools.ietf.org/html/rfc6238>`_
* `ROTP <https://github.com/mdp/rotp>`_ - Original Ruby OTP library by `Mark Percival <https://github.com/mdp>`_
* `OTPHP <https://github.com/lelag/otphp>`_ - PHP port of ROTP by `Le Lag <https://github.com/lelag>`_
+ * `OWASP Authentication Cheat Sheet <https://www.owasp.org/index.php/Authentication_Cheat_Sheet>`_
+ * `NIST SP 800-63-3: Digital Authentication Guideline <https://pages.nist.gov/800-63-3/>`_
.. image:: https://img.shields.io/travis/pyotp/pyotp.svg
:target: https://travis-ci.org/pyotp/pyotp
@@ -113,7 +123,7 @@ Description: PyOTP - The Python One-Time Password Library
.. image:: https://img.shields.io/pypi/l/pyotp.svg
:target: https://pypi.python.org/pypi/pyotp
.. image:: https://readthedocs.org/projects/pyotp/badge/?version=latest
- :target: https://pyotp.readthedocs.org/
+ :target: https://pyotp.readthedocs.io/
Platform: MacOS X
Platform: Posix
diff --git a/README.rst b/README.rst
index 7c5d90a..32bfec0 100644
--- a/README.rst
+++ b/README.rst
@@ -12,13 +12,15 @@ Authenticator <https://en.wikipedia.org/wiki/Google_Authenticator>`_, `Authy <ht
compatible app. Users can set up auth tokens in their apps easily by using their phone camera to scan `otpauth://
<https://github.com/google/google-authenticator/wiki/Key-Uri-Format>`_ QR codes provided by PyOTP.
+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.
+
Quick overview of using One Time Passwords on your phone
--------------------------------------------------------
* OTPs involve a shared secret, stored both on the phone and the server
* OTPs can be generated on a phone without internet connectivity
-* OTPs should always be used as a second factor of authentication(if your phone is lost, you account is still secured with a password)
-* Google Authenticator allows you to store multiple OTP secrets and provision those using a QR Code(no more typing in the secret)
+* OTPs should always be used as a second factor of authentication (if your phone is lost, you account is still secured with a password)
+* Google Authenticator and other OTP client apps allow you to store multiple OTP secrets and provision those using a QR Code
Installation
------------
@@ -29,7 +31,7 @@ Installation
Usage
-----
-Time based OTPs
+Time-based OTPs
~~~~~~~~~~~~~~~
::
@@ -58,23 +60,29 @@ Generating a base32 Secret Key
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
- pyotp.random_base32() # returns a 16 character base32 secret. Compatible with Google Authenticator
+ pyotp.random_base32() # returns a 16 character base32 secret. Compatible with Google Authenticator and other OTP apps
Google Authenticator Compatible
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The library works with the Google Authenticator iPhone and Android app, and also includes the ability to generate
-provisioning URI's for use with the QR Code scanner built into the app::
+PyOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. PyOTP includes the
+ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps::
+
+ pyotp.totp.TOTP('JBSWY3DPEHPK3PXP').provisioning_uri("alice at google.com", issuer_name="Secure App")
+
+ >>> 'otpauth://totp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure%20App'
+
+ pyotp.hotp.HOTP('JBSWY3DPEHPK3PXP').provisioning_uri("alice at google.com", initial_count=0, issuer_name="Secure App")
- totp.provisioning_uri("alice at google.com") # => 'otpauth://totp/alice@google.com?secret=JBSWY3DPEHPK3PXP'
- hotp.provisioning_uri("alice at google.com", 0) # => 'otpauth://hotp/alice@google.com?secret=JBSWY3DPEHPK3PXP&counter=0'
+ >>> 'otpauth://hotp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure%20App&counter=0'
-This can then be rendered as a QR Code which can then be scanned and added to the users list of OTP credentials.
+This URL can then be rendered as a QR Code (for example, using https://github.com/neocotic/qrious) which can then be scanned
+and added to the users list of OTP credentials.
Working example
~~~~~~~~~~~~~~~
-Scan the following barcode with your phone, using Google Authenticator
+Scan the following barcode with your phone's OTP app (e.g. Google Authenticator):
.. image:: http://chart.apis.google.com/chart?cht=qr&chs=250x250&chl=otpauth%3A%2F%2Ftotp%2Falice%40google.com%3Fsecret%3DJBSWY3DPEHPK3PXP
@@ -88,13 +96,15 @@ Links
~~~~~
* `Project home page (GitHub) <https://github.com/pyotp/pyotp>`_
-* `Documentation (Read the Docs) <https://pyotp.readthedocs.org/en/latest/>`_
+* `Documentation (Read the Docs) <https://pyotp.readthedocs.io/en/latest/>`_
* `Package distribution (PyPI) <https://pypi.python.org/pypi/pyotp>`_
* `Change log <https://github.com/pyotp/pyotp/blob/master/Changes.rst>`_
* `RFC 4226: HOTP: An HMAC-Based One-Time Password <https://tools.ietf.org/html/rfc4226>`_
* `RFC 6238: TOTP: Time-Based One-Time Password Algorithm <https://tools.ietf.org/html/rfc6238>`_
* `ROTP <https://github.com/mdp/rotp>`_ - Original Ruby OTP library by `Mark Percival <https://github.com/mdp>`_
* `OTPHP <https://github.com/lelag/otphp>`_ - PHP port of ROTP by `Le Lag <https://github.com/lelag>`_
+* `OWASP Authentication Cheat Sheet <https://www.owasp.org/index.php/Authentication_Cheat_Sheet>`_
+* `NIST SP 800-63-3: Digital Authentication Guideline <https://pages.nist.gov/800-63-3/>`_
.. image:: https://img.shields.io/travis/pyotp/pyotp.svg
:target: https://travis-ci.org/pyotp/pyotp
@@ -105,4 +115,4 @@ Links
.. image:: https://img.shields.io/pypi/l/pyotp.svg
:target: https://pypi.python.org/pypi/pyotp
.. image:: https://readthedocs.org/projects/pyotp/badge/?version=latest
- :target: https://pyotp.readthedocs.org/
+ :target: https://pyotp.readthedocs.io/
diff --git a/setup.cfg b/setup.cfg
index 4644c25..0069dd9 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -3,10 +3,9 @@ universal = 1
[flake8]
max-line-length = 120
-ignore = E301, E302, E401, E226, F841
+ignore = E301, E302, E305, E401, E226, F841
[egg_info]
tag_build =
tag_date = 0
-tag_svn_revision = 0
diff --git a/setup.py b/setup.py
index 27cf6a3..6451169 100755
--- a/setup.py
+++ b/setup.py
@@ -6,29 +6,29 @@ from setuptools import setup
install_requires = [line.rstrip() for line in open(os.path.join(os.path.dirname(__file__), "requirements.txt"))]
setup(
- name='pyotp',
- version='2.2.1',
- url='https://github.com/pyotp/pyotp',
- license='BSD License',
- author='PyOTP contributors',
- author_email='kislyuk at gmail.com',
- description='Python One Time Password Library',
- long_description=open('README.rst').read(),
+ name="pyotp",
+ version="2.2.6",
+ url="https://github.com/pyotp/pyotp",
+ license="BSD License",
+ author="PyOTP contributors",
+ author_email="kislyuk at gmail.com",
+ description="Python One Time Password Library",
+ long_description=open("README.rst").read(),
install_requires=install_requires,
- packages=['pyotp'],
- package_dir={'': 'src'},
- platforms=['MacOS X', 'Posix'],
+ packages=["pyotp"],
+ package_dir={"": "src"},
+ platforms=["MacOS X", "Posix"],
zip_safe=False,
test_suite="test",
classifiers=[
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: BSD License',
- 'Operating System :: MacOS :: MacOS X',
- 'Operating System :: POSIX',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3.3',
- 'Programming Language :: Python :: 3.4',
- 'Topic :: Software Development :: Libraries :: Python Modules'
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: BSD License",
+ "Operating System :: MacOS :: MacOS X",
+ "Operating System :: POSIX",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 2.7",
+ "Programming Language :: Python :: 3.3",
+ "Programming Language :: Python :: 3.4",
+ "Topic :: Software Development :: Libraries :: Python Modules"
]
)
diff --git a/src/pyotp.egg-info/PKG-INFO b/src/pyotp.egg-info/PKG-INFO
index 1a62fa1..7a8312d 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.2.1
+Version: 2.2.6
Summary: Python One Time Password Library
Home-page: https://github.com/pyotp/pyotp
Author: PyOTP contributors
@@ -20,13 +20,15 @@ Description: PyOTP - The Python One-Time Password Library
compatible app. Users can set up auth tokens in their apps easily by using their phone camera to scan `otpauth://
<https://github.com/google/google-authenticator/wiki/Key-Uri-Format>`_ QR codes provided by PyOTP.
+ 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.
+
Quick overview of using One Time Passwords on your phone
--------------------------------------------------------
* OTPs involve a shared secret, stored both on the phone and the server
* OTPs can be generated on a phone without internet connectivity
- * OTPs should always be used as a second factor of authentication(if your phone is lost, you account is still secured with a password)
- * Google Authenticator allows you to store multiple OTP secrets and provision those using a QR Code(no more typing in the secret)
+ * OTPs should always be used as a second factor of authentication (if your phone is lost, you account is still secured with a password)
+ * Google Authenticator and other OTP client apps allow you to store multiple OTP secrets and provision those using a QR Code
Installation
------------
@@ -37,7 +39,7 @@ Description: PyOTP - The Python One-Time Password Library
Usage
-----
- Time based OTPs
+ Time-based OTPs
~~~~~~~~~~~~~~~
::
@@ -66,23 +68,29 @@ Description: PyOTP - The Python One-Time Password Library
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
- pyotp.random_base32() # returns a 16 character base32 secret. Compatible with Google Authenticator
+ pyotp.random_base32() # returns a 16 character base32 secret. Compatible with Google Authenticator and other OTP apps
Google Authenticator Compatible
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- The library works with the Google Authenticator iPhone and Android app, and also includes the ability to generate
- provisioning URI's for use with the QR Code scanner built into the app::
+ PyOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. PyOTP includes the
+ ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps::
+
+ pyotp.totp.TOTP('JBSWY3DPEHPK3PXP').provisioning_uri("alice at google.com", issuer_name="Secure App")
+
+ >>> 'otpauth://totp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure%20App'
+
+ pyotp.hotp.HOTP('JBSWY3DPEHPK3PXP').provisioning_uri("alice at google.com", initial_count=0, issuer_name="Secure App")
- totp.provisioning_uri("alice at google.com") # => 'otpauth://totp/alice@google.com?secret=JBSWY3DPEHPK3PXP'
- hotp.provisioning_uri("alice at google.com", 0) # => 'otpauth://hotp/alice@google.com?secret=JBSWY3DPEHPK3PXP&counter=0'
+ >>> 'otpauth://hotp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure%20App&counter=0'
- This can then be rendered as a QR Code which can then be scanned and added to the users list of OTP credentials.
+ This URL can then be rendered as a QR Code (for example, using https://github.com/neocotic/qrious) which can then be scanned
+ and added to the users list of OTP credentials.
Working example
~~~~~~~~~~~~~~~
- Scan the following barcode with your phone, using Google Authenticator
+ Scan the following barcode with your phone's OTP app (e.g. Google Authenticator):
.. image:: http://chart.apis.google.com/chart?cht=qr&chs=250x250&chl=otpauth%3A%2F%2Ftotp%2Falice%40google.com%3Fsecret%3DJBSWY3DPEHPK3PXP
@@ -96,13 +104,15 @@ Description: PyOTP - The Python One-Time Password Library
~~~~~
* `Project home page (GitHub) <https://github.com/pyotp/pyotp>`_
- * `Documentation (Read the Docs) <https://pyotp.readthedocs.org/en/latest/>`_
+ * `Documentation (Read the Docs) <https://pyotp.readthedocs.io/en/latest/>`_
* `Package distribution (PyPI) <https://pypi.python.org/pypi/pyotp>`_
* `Change log <https://github.com/pyotp/pyotp/blob/master/Changes.rst>`_
* `RFC 4226: HOTP: An HMAC-Based One-Time Password <https://tools.ietf.org/html/rfc4226>`_
* `RFC 6238: TOTP: Time-Based One-Time Password Algorithm <https://tools.ietf.org/html/rfc6238>`_
* `ROTP <https://github.com/mdp/rotp>`_ - Original Ruby OTP library by `Mark Percival <https://github.com/mdp>`_
* `OTPHP <https://github.com/lelag/otphp>`_ - PHP port of ROTP by `Le Lag <https://github.com/lelag>`_
+ * `OWASP Authentication Cheat Sheet <https://www.owasp.org/index.php/Authentication_Cheat_Sheet>`_
+ * `NIST SP 800-63-3: Digital Authentication Guideline <https://pages.nist.gov/800-63-3/>`_
.. image:: https://img.shields.io/travis/pyotp/pyotp.svg
:target: https://travis-ci.org/pyotp/pyotp
@@ -113,7 +123,7 @@ Description: PyOTP - The Python One-Time Password Library
.. image:: https://img.shields.io/pypi/l/pyotp.svg
:target: https://pypi.python.org/pypi/pyotp
.. image:: https://readthedocs.org/projects/pyotp/badge/?version=latest
- :target: https://pyotp.readthedocs.org/
+ :target: https://pyotp.readthedocs.io/
Platform: MacOS X
Platform: Posix
diff --git a/src/pyotp/hotp.py b/src/pyotp/hotp.py
index 40ddcfa..d65199b 100644
--- a/src/pyotp/hotp.py
+++ b/src/pyotp/hotp.py
@@ -5,32 +5,48 @@ from .otp import OTP
from .compat import str
class HOTP(OTP):
+ """
+ Handler for HMAC-based OTP counters.
+ """
def at(self, count):
"""
- Generates the OTP for the given count
- @param [Integer] count counter
- @returns [Integer] OTP
+ Generates the OTP for the given count.
+
+ :param count: the OTP HMAC counter
+ :type count: int
+ :returns: OTP
+ :rtype: str
"""
return self.generate_otp(count)
def verify(self, otp, counter):
"""
- Verifies the OTP passed in against the current time OTP
- @param [String/Integer] otp the OTP to check against
- @param [Integer] counter the counter of the OTP
+ Verifies the OTP passed in against the current time OTP.
+
+ :param otp: the OTP to check against
+ :type otp: str
+ :param count: the OTP HMAC counter
+ :type count: int
"""
return utils.strings_equal(str(otp), str(self.at(counter)))
def provisioning_uri(self, name, initial_count=0, issuer_name=None):
"""
- Returns the provisioning URI for the OTP
- This can then be encoded in a QR Code and used
- to provision the Google Authenticator app
- @param [String] name of the account
- @param [Integer] initial_count starting counter value, defaults to 0
- @param [String] the name of the OTP issuer; this will be the
+ Returns the provisioning URI for the OTP. This can then be
+ encoded in a QR Code and used to provision an OTP app like
+ Google Authenticator.
+
+ See also:
+ https://github.com/google/google-authenticator/wiki/Key-Uri-Format
+
+ :param name: name of the user account
+ :type name: str
+ :param initial_count: starting HMAC counter value, defaults to 0
+ :type initial_count: int
+ :param issuer_name: the name of the OTP issuer; this will be the
organization title of the OTP entry in Authenticator
- @return [String] provisioning uri
+ :returns: provisioning URI
+ :rtype: str
"""
return utils.build_uri(
self.secret,
diff --git a/src/pyotp/otp.py b/src/pyotp/otp.py
index f0fe49f..45afcf3 100644
--- a/src/pyotp/otp.py
+++ b/src/pyotp/otp.py
@@ -6,16 +6,17 @@ import hmac
from .compat import str
class OTP(object):
+ """
+ Base class for OTP handlers.
+ """
def __init__(self, s, digits=6, digest=hashlib.sha1):
"""
- @param [String] secret in the form of base32
- @option options digits [Integer] (6)
- Number of integers in the OTP
- Google Authenticate only supports 6 currently
- @option options digest [Callable] (hashlib.sha1)
- Digest used in the HMAC
- Google Authenticate only supports 'sha1' currently
- @returns [OTP] OTP instantiation
+ :param s: secret in base32 format
+ :type s: str
+ :param digits: number of integers in the OTP. Some apps expect this to be 6 digits, others support more.
+ :type digits: int
+ :param digest: digest function to use in the HMAC (expected to be sha1)
+ :type digest: callable
"""
self.digits = digits
self.digest = digest
@@ -23,10 +24,12 @@ class OTP(object):
def generate_otp(self, input):
"""
- @param [Integer] input the number used seed the HMAC
- Usually either the counter, or the computed integer
- based on the Unix timestamp
+ :param input: the HMAC counter value to use as the OTP input.
+ Usually either the counter, or the computed integer based on the Unix timestamp
+ :type input: int
"""
+ if input < 0:
+ raise ValueError('input must be positive integer')
hasher = hmac.new(self.byte_secret(), self.int_to_bytestring(input), self.digest)
hmac_hash = bytearray(hasher.digest())
offset = hmac_hash[-1] & 0xf
diff --git a/src/pyotp/totp.py b/src/pyotp/totp.py
index 27f81b8..50faf8b 100644
--- a/src/pyotp/totp.py
+++ b/src/pyotp/totp.py
@@ -8,20 +8,27 @@ from .otp import OTP
from .compat import str
class TOTP(OTP):
+ """
+ Handler for time-based OTP counters.
+ """
def __init__(self, *args, **kwargs):
"""
- @option options [Integer] interval (30) the time interval in seconds
- for OTP This defaults to 30 which is standard.
+ :param interval: the time interval in seconds
+ for OTP. This defaults to 30.
+ :type interval: int
"""
self.interval = kwargs.pop('interval', 30)
super(TOTP, self).__init__(*args, **kwargs)
def at(self, for_time, counter_offset=0):
"""
- Accepts either a Unix timestamp integer or a Time object.
- @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
+ Accepts either a Unix timestamp integer or a datetime object.
+
+ :param for_time: the time to generate an OTP for
+ :type for_time: int or datetime
+ :param counter_offset: the amount of ticks to add to the time counter
+ :returns: OTP value
+ :rtype: str
"""
if not isinstance(for_time, datetime.datetime):
for_time = datetime.datetime.fromtimestamp(int(for_time))
@@ -30,16 +37,24 @@ class TOTP(OTP):
def now(self):
"""
Generate the current time OTP
- @return [Integer] the OTP as an integer
+
+ :returns: OTP value
+ :rtype: str
"""
return self.generate_otp(self.timecode(datetime.datetime.now()))
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
+ Verifies the OTP passed in against the current time OTP.
+
+ :param otp: the OTP to check against
+ :type otp: str
+ :param for_time: Time to check OTP at (defaults to now)
+ :type for_time: int or datetime
+ :param valid_window: extends the validity to this many counter ticks before and after the current one
+ :type valid_window: int
+ :returns: True if verification succeeded, False otherwise
+ :rtype: bool
"""
if for_time is None:
for_time = datetime.datetime.now()
@@ -54,11 +69,19 @@ class TOTP(OTP):
def provisioning_uri(self, name, issuer_name=None):
"""
- Returns the provisioning URI for the OTP
- This can then be encoded in a QR Code and used
- to provision the Google Authenticator app
- @param [String] name of the account
- @return [String] provisioning uri
+ Returns the provisioning URI for the OTP. This can then be
+ encoded in a QR Code and used to provision an OTP app like
+ Google Authenticator.
+
+ See also:
+ https://github.com/google/google-authenticator/wiki/Key-Uri-Format
+
+ :param name: name of the user account
+ :type name: str
+ :param issuer_name: the name of the OTP issuer; this will be the
+ organization title of the OTP entry in Authenticator
+ :returns: provisioning URI
+ :rtype: str
"""
return utils.build_uri(self.secret, name, issuer_name=issuer_name,
algorithm=self.digest().name,
diff --git a/src/pyotp/utils.py b/src/pyotp/utils.py
index 16a13d5..1e52064 100644
--- a/src/pyotp/utils.py
+++ b/src/pyotp/utils.py
@@ -25,17 +25,25 @@ def build_uri(secret, name, initial_count=None, issuer_name=None,
See also:
https://github.com/google/google-authenticator/wiki/Key-Uri-Format
- @param [String] the hotp/totp secret used to generate the URI
- @param [String] name of the account
- @param [Integer] initial_count starting counter value, defaults to None.
+ :param secret: the hotp/totp secret used to generate the URI
+ :type secret: str
+ :param name: name of the account
+ :type name: str
+ :param initial_count: starting counter value, defaults to None.
If none, the OTP type will be assumed as TOTP.
- @param [String] the name of the OTP issuer; this will be the
+ :type initial_count: int
+ :param issuer_name: the name of the OTP issuer; this will be the
organization title of the OTP entry in Authenticator
- @param [String] the algorithm used in the OTP generation.
- @param [Integer] the length of the OTP generated code.
- @param [Integer] the number of seconds the OTP generator is set to
+ :type issuer_name: str
+ :param algorithm: the algorithm used in the OTP generation.
+ :type algorithm: str
+ :param digits: the length of the OTP generated code.
+ :type digits: int
+ :param period: the number of seconds the OTP generator is set to
expire every code.
- @return [String] provisioning uri
+ :type period: int
+ :returns: provisioning uri
+ :rtype: str
"""
# initial_count may be 0 as a valid param
is_initial_count_present = (initial_count is not None)
@@ -46,7 +54,7 @@ def build_uri(secret, name, initial_count=None, issuer_name=None,
is_period_set = (period is not None and period != 30)
otp_type = 'hotp' if is_initial_count_present else 'totp'
- base_uri = 'otpauth://{}/{}?{}'
+ base_uri = 'otpauth://{0}/{1}?{2}'
url_args = {'secret': secret}
@@ -64,7 +72,7 @@ def build_uri(secret, name, initial_count=None, issuer_name=None,
if is_period_set:
url_args['period'] = period
- uri = base_uri.format(otp_type, label, urlencode(url_args))
+ uri = base_uri.format(otp_type, label, urlencode(url_args).replace("+", "%20"))
return uri
diff --git a/test.py b/test.py
index 395d475..d3cbc07 100755
--- a/test.py
+++ b/test.py
@@ -3,12 +3,9 @@
from __future__ import absolute_import, division, print_function, unicode_literals
-import base64
-import datetime
-import hashlib
-import os
-import sys
-import unittest
+import base64, datetime, hashlib, os, sys, unittest
+from warnings import warn
+
try:
from urllib.parse import urlparse, parse_qsl
except ImportError:
@@ -33,6 +30,11 @@ class HOTPExampleValuesFromTheRFC(unittest.TestCase):
self.assertEqual(hotp.at(8), '399871')
self.assertEqual(hotp.at(9), '520489')
+ def test_invalid_input(self):
+ hotp = pyotp.HOTP('GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ')
+ with self.assertRaises(ValueError):
+ hotp.at(-1)
+
def test_verify_otp_reuse(self):
hotp = pyotp.HOTP('GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ')
self.assertTrue(hotp.verify('520489', 9))
@@ -81,14 +83,14 @@ class HOTPExampleValuesFromTheRFC(unittest.TestCase):
hotp = pyotp.HOTP(key, digits=8)
url = urlparse(
- hotp.provisioning_uri('baco at peperina', issuer_name='FooCorp',
+ hotp.provisioning_uri('baco at peperina', issuer_name='Foo Corp',
initial_count=10))
self.assertEqual(url.scheme, 'otpauth')
self.assertEqual(url.netloc, 'hotp')
- self.assertEqual(url.path, '/FooCorp:baco%40peperina')
+ self.assertEqual(url.path, '/Foo%20Corp:baco%40peperina')
self.assertEqual(dict(parse_qsl(url.query)),
{'secret': 'c7uxuqhgflpw7oruedmglbrk7u6242vb',
- 'counter': '10', 'issuer': 'FooCorp',
+ 'counter': '10', 'issuer': 'Foo Corp',
'digits': '8'})
def test_other_secret(self):
@@ -137,9 +139,9 @@ class TOTPExampleValuesFromTheRFC(unittest.TestCase):
for digest, secret in self.RFC_VALUES:
totp = pyotp.TOTP(base64.b32encode(secret), 8, digest)
for utime, code in self.RFC_VALUES[(digest, secret)]:
- # 32-bit platforms use native functions to handle timestamps, so they fail this test
- # (and will fail after 19 January 2038)
if utime > sys.maxsize:
+ warn("32-bit platforms use native functions to handle timestamps, so they fail this test" +
+ " (and will fail after 19 January 2038)")
continue
value = totp.at(utime)
msg = "%s != %s (%s, time=%d)"
@@ -165,6 +167,14 @@ class TOTPExampleValuesFromTheRFC(unittest.TestCase):
with Timecop(1297553958 + 30):
self.assertFalse(totp.verify('102705'))
+ def test_input_before_epoch(self):
+ totp = pyotp.TOTP('GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ')
+ # -1 and -29.5 round down to 0 (epoch)
+ self.assertEqual(totp.at(-1), '755224')
+ self.assertEqual(totp.at(-29.5), '755224')
+ with self.assertRaises(ValueError):
+ totp.at(-30)
+
def test_validate_totp_with_digit_length(self):
totp = pyotp.TOTP('GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ')
with Timecop(1111111111):
--
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