[Python-modules-commits] [python-pyotp] 01/03: Imported Upstream version 2.0.1

Hugo Lefeuvre hle at moszumanska.debian.org
Tue Jul 5 13:45:11 UTC 2016


This is an automated email from the git hooks/post-receive script.

hle pushed a commit to branch master
in repository python-pyotp.

commit 263b24369c88f405e8f7387e96a14c59b09f6d5f
Author: Hugo Lefeuvre <hle at debian.org>
Date:   Tue Jul 5 15:36:35 2016 +0200

    Imported Upstream version 2.0.1
---
 LICENSE                                 |  21 ++++
 MANIFEST.in                             |   4 +
 PKG-INFO                                | 130 ++++++++++++++++++++++++
 README.rst                              | 110 ++++++++++++++++++++
 requirements.txt                        |   0
 setup.cfg                               |   8 ++
 setup.py                                |  33 ++++++
 src/pyotp.egg-info/PKG-INFO             | 130 ++++++++++++++++++++++++
 src/pyotp.egg-info/SOURCES.txt          |  17 ++++
 src/pyotp.egg-info/dependency_links.txt |   1 +
 src/pyotp.egg-info/not-zip-safe         |   1 +
 src/pyotp.egg-info/top_level.txt        |   1 +
 src/pyotp/__init__.py                   |  18 ++++
 src/pyotp/hotp.py                       |  40 ++++++++
 src/pyotp/otp.py                        |  64 ++++++++++++
 src/pyotp/totp.py                       |  58 +++++++++++
 src/pyotp/utils.py                      |  79 +++++++++++++++
 test.py                                 | 171 ++++++++++++++++++++++++++++++++
 18 files changed, 886 insertions(+)

diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c6b2e22
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+Copyright (C) 2011 by Mark Percival <m at mdp.im>
+
+Python port copyright 2011 by Nathan Reynolds <email at nreynolds.co.uk>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..d1380ed
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,4 @@
+include LICENSE
+include README.markdown
+include requirements.txt
+include test.py
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..c77fda1
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,130 @@
+Metadata-Version: 1.1
+Name: pyotp
+Version: 2.0.1
+Summary: Python One Time Password Library
+Home-page: https://github.com/pyotp/pyotp
+Author: PyOTP contributors
+Author-email: kislyuk at gmail.com
+License: BSD License
+Description: PyOTP - The Python One-Time Password Library
+        ============================================
+        
+        PyOTP is a Python library for generating and verifying one-time passwords. It can be used to implement two-factor (2FA)
+        or multi-factor (MFA) authentication methods in web applications and in other systems that require users to log in.
+        
+        Open MFA standards are defined in `RFC 4226 <https://tools.ietf.org/html/rfc4226>`_ (HOTP: An HMAC-Based One-Time
+        Password Algorithm) and in `RFC 6238 <https://tools.ietf.org/html/rfc6238>`_ (TOTP: Time-Based One-Time Password
+        Algorithm). PyOTP implements server-side support for both of these standards. Client-side support can be enabled by
+        sending authentication codes to users over SMS or email (HOTP) or, for TOTP, by instructing users to use `Google
+        Authenticator <https://en.wikipedia.org/wiki/Google_Authenticator>`_, `Authy <https://www.authy.com/>`_, or another
+        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.
+        
+        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)
+        
+        Installation
+        ------------
+        ::
+        
+            pip install pyotp
+        
+        Usage
+        -----
+        
+        Time based OTPs
+        ~~~~~~~~~~~~~~~
+        ::
+        
+            totp = pyotp.TOTP('base32secret3232')
+            totp.now() # => 492039
+        
+            # OTP verified for current time
+            totp.verify(492039) # => True
+            time.sleep(30)
+            totp.verify(492039) # => False
+        
+        Counter-based OTPs
+        ~~~~~~~~~~~~~~~~~~
+        ::
+        
+            hotp = pyotp.HOTP('base32secret3232')
+            hotp.at(0) # => 260182
+            hotp.at(1) # => 55283
+            hotp.at(1401) # => 316439
+        
+            # OTP verified with a counter
+            hotp.verify(316439, 1401) # => True
+            hotp.verify(316439, 1402) # => False
+        
+        Generating a base32 Secret Key
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+        ::
+        
+            pyotp.random_base32() # returns a 16 character base32 secret. Compatible with Google Authenticator
+        
+        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::
+        
+            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'
+        
+        This can then be rendered as a QR Code 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
+        
+        ![QR Code for OTP](http://chart.apis.google.com/chart?cht=qr&chs=250x250&chl=otpauth%3A%2F%2Ftotp%2Falice%40google.com%3Fsecret%3DJBSWY3DPEHPK3PXP)
+        
+        Now run the following and compare the output::
+        
+            import pyotp
+            totp = pyotp.TOTP("JBSWY3DPEHPK3PXP")
+            print "Current OTP: %s" % totp.now()
+        
+        Links
+        ~~~~~
+        
+        * `Project home page (GitHub) <https://github.com/pyotp/pyotp>`_
+        * `Documentation (Read the Docs) <https://pyotp.readthedocs.org/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>`_
+        
+        .. image:: https://img.shields.io/travis/pyotp/pyotp.svg
+                :target: https://travis-ci.org/pyotp/pyotp
+        .. image:: https://img.shields.io/coveralls/pyotp/pyotp.svg
+                :target: https://coveralls.io/r/pyotp/pyotp?branch=master
+        .. image:: https://img.shields.io/pypi/v/pyotp.svg
+                :target: https://pypi.python.org/pypi/pyotp
+        .. image:: https://img.shields.io/pypi/dm/pyotp.svg
+                :target: https://pypi.python.org/pypi/pyotp
+        .. 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/
+        
+Platform: MacOS X
+Platform: Posix
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Operating System :: POSIX
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..8d89f0b
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,110 @@
+PyOTP - The Python One-Time Password Library
+============================================
+
+PyOTP is a Python library for generating and verifying one-time passwords. It can be used to implement two-factor (2FA)
+or multi-factor (MFA) authentication methods in web applications and in other systems that require users to log in.
+
+Open MFA standards are defined in `RFC 4226 <https://tools.ietf.org/html/rfc4226>`_ (HOTP: An HMAC-Based One-Time
+Password Algorithm) and in `RFC 6238 <https://tools.ietf.org/html/rfc6238>`_ (TOTP: Time-Based One-Time Password
+Algorithm). PyOTP implements server-side support for both of these standards. Client-side support can be enabled by
+sending authentication codes to users over SMS or email (HOTP) or, for TOTP, by instructing users to use `Google
+Authenticator <https://en.wikipedia.org/wiki/Google_Authenticator>`_, `Authy <https://www.authy.com/>`_, or another
+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.
+
+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)
+
+Installation
+------------
+::
+
+    pip install pyotp
+
+Usage
+-----
+
+Time based OTPs
+~~~~~~~~~~~~~~~
+::
+
+    totp = pyotp.TOTP('base32secret3232')
+    totp.now() # => 492039
+
+    # OTP verified for current time
+    totp.verify(492039) # => True
+    time.sleep(30)
+    totp.verify(492039) # => False
+
+Counter-based OTPs
+~~~~~~~~~~~~~~~~~~
+::
+
+    hotp = pyotp.HOTP('base32secret3232')
+    hotp.at(0) # => 260182
+    hotp.at(1) # => 55283
+    hotp.at(1401) # => 316439
+
+    # OTP verified with a counter
+    hotp.verify(316439, 1401) # => True
+    hotp.verify(316439, 1402) # => False
+
+Generating a base32 Secret Key
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+::
+
+    pyotp.random_base32() # returns a 16 character base32 secret. Compatible with Google Authenticator
+
+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::
+
+    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'
+
+This can then be rendered as a QR Code 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
+
+![QR Code for OTP](http://chart.apis.google.com/chart?cht=qr&chs=250x250&chl=otpauth%3A%2F%2Ftotp%2Falice%40google.com%3Fsecret%3DJBSWY3DPEHPK3PXP)
+
+Now run the following and compare the output::
+
+    import pyotp
+    totp = pyotp.TOTP("JBSWY3DPEHPK3PXP")
+    print "Current OTP: %s" % totp.now()
+
+Links
+~~~~~
+
+* `Project home page (GitHub) <https://github.com/pyotp/pyotp>`_
+* `Documentation (Read the Docs) <https://pyotp.readthedocs.org/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>`_
+
+.. image:: https://img.shields.io/travis/pyotp/pyotp.svg
+        :target: https://travis-ci.org/pyotp/pyotp
+.. image:: https://img.shields.io/coveralls/pyotp/pyotp.svg
+        :target: https://coveralls.io/r/pyotp/pyotp?branch=master
+.. image:: https://img.shields.io/pypi/v/pyotp.svg
+        :target: https://pypi.python.org/pypi/pyotp
+.. image:: https://img.shields.io/pypi/dm/pyotp.svg
+        :target: https://pypi.python.org/pypi/pyotp
+.. 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/
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..e69de29
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..6f08d0e
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,8 @@
+[bdist_wheel]
+universal = 1
+
+[egg_info]
+tag_build = 
+tag_date = 0
+tag_svn_revision = 0
+
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..06ddcb6
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+import os, glob
+from setuptools import setup, find_packages
+
+install_requires = [line.rstrip() for line in open(os.path.join(os.path.dirname(__file__), "requirements.txt"))]
+
+setup(
+    name='pyotp',
+    version='2.0.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(),
+    install_requires=install_requires,
+    packages=['pyotp'],
+    package_dir={'': 'src'},
+    platforms=['MacOS X', 'Posix'],
+    zip_safe=False,
+    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'
+    ]
+)
diff --git a/src/pyotp.egg-info/PKG-INFO b/src/pyotp.egg-info/PKG-INFO
new file mode 100644
index 0000000..c77fda1
--- /dev/null
+++ b/src/pyotp.egg-info/PKG-INFO
@@ -0,0 +1,130 @@
+Metadata-Version: 1.1
+Name: pyotp
+Version: 2.0.1
+Summary: Python One Time Password Library
+Home-page: https://github.com/pyotp/pyotp
+Author: PyOTP contributors
+Author-email: kislyuk at gmail.com
+License: BSD License
+Description: PyOTP - The Python One-Time Password Library
+        ============================================
+        
+        PyOTP is a Python library for generating and verifying one-time passwords. It can be used to implement two-factor (2FA)
+        or multi-factor (MFA) authentication methods in web applications and in other systems that require users to log in.
+        
+        Open MFA standards are defined in `RFC 4226 <https://tools.ietf.org/html/rfc4226>`_ (HOTP: An HMAC-Based One-Time
+        Password Algorithm) and in `RFC 6238 <https://tools.ietf.org/html/rfc6238>`_ (TOTP: Time-Based One-Time Password
+        Algorithm). PyOTP implements server-side support for both of these standards. Client-side support can be enabled by
+        sending authentication codes to users over SMS or email (HOTP) or, for TOTP, by instructing users to use `Google
+        Authenticator <https://en.wikipedia.org/wiki/Google_Authenticator>`_, `Authy <https://www.authy.com/>`_, or another
+        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.
+        
+        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)
+        
+        Installation
+        ------------
+        ::
+        
+            pip install pyotp
+        
+        Usage
+        -----
+        
+        Time based OTPs
+        ~~~~~~~~~~~~~~~
+        ::
+        
+            totp = pyotp.TOTP('base32secret3232')
+            totp.now() # => 492039
+        
+            # OTP verified for current time
+            totp.verify(492039) # => True
+            time.sleep(30)
+            totp.verify(492039) # => False
+        
+        Counter-based OTPs
+        ~~~~~~~~~~~~~~~~~~
+        ::
+        
+            hotp = pyotp.HOTP('base32secret3232')
+            hotp.at(0) # => 260182
+            hotp.at(1) # => 55283
+            hotp.at(1401) # => 316439
+        
+            # OTP verified with a counter
+            hotp.verify(316439, 1401) # => True
+            hotp.verify(316439, 1402) # => False
+        
+        Generating a base32 Secret Key
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+        ::
+        
+            pyotp.random_base32() # returns a 16 character base32 secret. Compatible with Google Authenticator
+        
+        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::
+        
+            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'
+        
+        This can then be rendered as a QR Code 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
+        
+        ![QR Code for OTP](http://chart.apis.google.com/chart?cht=qr&chs=250x250&chl=otpauth%3A%2F%2Ftotp%2Falice%40google.com%3Fsecret%3DJBSWY3DPEHPK3PXP)
+        
+        Now run the following and compare the output::
+        
+            import pyotp
+            totp = pyotp.TOTP("JBSWY3DPEHPK3PXP")
+            print "Current OTP: %s" % totp.now()
+        
+        Links
+        ~~~~~
+        
+        * `Project home page (GitHub) <https://github.com/pyotp/pyotp>`_
+        * `Documentation (Read the Docs) <https://pyotp.readthedocs.org/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>`_
+        
+        .. image:: https://img.shields.io/travis/pyotp/pyotp.svg
+                :target: https://travis-ci.org/pyotp/pyotp
+        .. image:: https://img.shields.io/coveralls/pyotp/pyotp.svg
+                :target: https://coveralls.io/r/pyotp/pyotp?branch=master
+        .. image:: https://img.shields.io/pypi/v/pyotp.svg
+                :target: https://pypi.python.org/pypi/pyotp
+        .. image:: https://img.shields.io/pypi/dm/pyotp.svg
+                :target: https://pypi.python.org/pypi/pyotp
+        .. 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/
+        
+Platform: MacOS X
+Platform: Posix
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Operating System :: POSIX
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff --git a/src/pyotp.egg-info/SOURCES.txt b/src/pyotp.egg-info/SOURCES.txt
new file mode 100644
index 0000000..13f1e12
--- /dev/null
+++ b/src/pyotp.egg-info/SOURCES.txt
@@ -0,0 +1,17 @@
+LICENSE
+MANIFEST.in
+README.rst
+requirements.txt
+setup.cfg
+setup.py
+test.py
+src/pyotp/__init__.py
+src/pyotp/hotp.py
+src/pyotp/otp.py
+src/pyotp/totp.py
+src/pyotp/utils.py
+src/pyotp.egg-info/PKG-INFO
+src/pyotp.egg-info/SOURCES.txt
+src/pyotp.egg-info/dependency_links.txt
+src/pyotp.egg-info/not-zip-safe
+src/pyotp.egg-info/top_level.txt
\ No newline at end of file
diff --git a/src/pyotp.egg-info/dependency_links.txt b/src/pyotp.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/pyotp.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/src/pyotp.egg-info/not-zip-safe b/src/pyotp.egg-info/not-zip-safe
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/pyotp.egg-info/not-zip-safe
@@ -0,0 +1 @@
+
diff --git a/src/pyotp.egg-info/top_level.txt b/src/pyotp.egg-info/top_level.txt
new file mode 100644
index 0000000..6c1907d
--- /dev/null
+++ b/src/pyotp.egg-info/top_level.txt
@@ -0,0 +1 @@
+pyotp
diff --git a/src/pyotp/__init__.py b/src/pyotp/__init__.py
new file mode 100644
index 0000000..2c661b2
--- /dev/null
+++ b/src/pyotp/__init__.py
@@ -0,0 +1,18 @@
+from __future__ import print_function, unicode_literals, division, absolute_import
+
+import random as _random
+
+from pyotp.hotp import HOTP
+from pyotp.otp import OTP
+from pyotp.totp import TOTP
+from . import utils
+
+VERSION = '1.4.2'
+
+
+def random_base32(length=16, random=_random.SystemRandom(),
+                  chars=list('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')):
+    return ''.join(
+        random.choice(chars)
+        for _ in range(length)
+    )
diff --git a/src/pyotp/hotp.py b/src/pyotp/hotp.py
new file mode 100644
index 0000000..5a06b1a
--- /dev/null
+++ b/src/pyotp/hotp.py
@@ -0,0 +1,40 @@
+from __future__ import print_function, unicode_literals, division, absolute_import
+
+from pyotp.otp import OTP
+from pyotp import utils
+
+
+class HOTP(OTP):
+    def at(self, count):
+        """
+        Generates the OTP for the given count
+        @param [Integer] count counter
+        @returns [Integer] OTP
+        """
+        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
+        """
+        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
+            organization title of the OTP entry in Authenticator
+        @return [String] provisioning uri
+        """
+        return utils.build_uri(
+            self.secret,
+            name,
+            initial_count=initial_count,
+            issuer_name=issuer_name,
+        )
diff --git a/src/pyotp/otp.py b/src/pyotp/otp.py
new file mode 100644
index 0000000..bd61fd0
--- /dev/null
+++ b/src/pyotp/otp.py
@@ -0,0 +1,64 @@
+from __future__ import print_function, unicode_literals, division, absolute_import
+
+import base64
+import hashlib
+import hmac
+
+
+class OTP(object):
+    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
+        """
+        self.digits = digits
+        self.digest = digest
+        self.secret = s
+
+    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
+        """
+        hmac_hash = hmac.new(
+            self.byte_secret(),
+            self.int_to_bytestring(input),
+            self.digest,
+        ).digest()
+
+        hmac_hash = bytearray(hmac_hash)
+        offset = hmac_hash[-1] & 0xf
+        code = ((hmac_hash[offset] & 0x7f) << 24 |
+                (hmac_hash[offset + 1] & 0xff) << 16 |
+                (hmac_hash[offset + 2] & 0xff) << 8 |
+                (hmac_hash[offset + 3] & 0xff))
+        str_code = str(code % 10 ** self.digits)
+        while len(str_code) < self.digits:
+            str_code = '0' + str_code
+
+        return str_code
+
+    def byte_secret(self):
+        return base64.b32decode(self.secret, casefold=True)
+
+    @staticmethod
+    def int_to_bytestring(i, padding=8):
+        """
+        Turns an integer to the OATH specified
+        bytestring, which is fed to the HMAC
+        along with the secret
+        """
+        result = bytearray()
+        while i != 0:
+            result.append(i & 0xFF)
+            i >>= 8
+        # It's necessary to convert the final result from bytearray to bytes because
+        # the hmac functions in python 2.6 and 3.3 don't work with bytearray
+        return bytes(bytearray(reversed(result)).rjust(padding, b'\0'))
diff --git a/src/pyotp/totp.py b/src/pyotp/totp.py
new file mode 100644
index 0000000..59aedf6
--- /dev/null
+++ b/src/pyotp/totp.py
@@ -0,0 +1,58 @@
+from __future__ import print_function, unicode_literals, division, absolute_import
+
+import datetime
+import time
+
+from pyotp import utils
+from pyotp.otp import OTP
+
+
+class TOTP(OTP):
+    def __init__(self, *args, **kwargs):
+        """
+        @option options [Integer] interval (30) the time interval in seconds
+            for OTP This defaults to 30 which is standard.
+        """
+        self.interval = kwargs.pop('interval', 30)
+        super(TOTP, self).__init__(*args, **kwargs)
+
+    def at(self, for_time):
+        """
+        Accepts either a Unix timestamp integer or a Time object.
+        Time objects will be adjusted to UTC automatically
+        @param [Time/Integer] time the time to generate an OTP for
+        """
+        if not isinstance(for_time, datetime.datetime):
+            for_time = datetime.datetime.fromtimestamp(int(for_time))
+        return self.generate_otp(self.timecode(for_time))
+
+    def now(self):
+        """
+        Generate the current time OTP
+        @return [Integer] the OTP as an integer
+        """
+        return self.generate_otp(self.timecode(datetime.datetime.now()))
+
+    def verify(self, otp, for_time=None):
+        """
+        Verifies the OTP passed in against the current time OTP
+        @param [String/Integer] otp the OTP to check against
+        """
+        if for_time is None:
+            for_time = datetime.datetime.now()
+
+        return utils.strings_equal(str(otp), str(self.at(for_time)))
+
+    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
+        """
+        return utils.build_uri(self.secret, name, issuer_name=issuer_name)
+
+    def timecode(self, for_time):
+        i = time.mktime(for_time.timetuple())
+        return int(i / self.interval)
diff --git a/src/pyotp/utils.py b/src/pyotp/utils.py
new file mode 100644
index 0000000..2280fdf
--- /dev/null
+++ b/src/pyotp/utils.py
@@ -0,0 +1,79 @@
+from __future__ import print_function, unicode_literals, division, absolute_import
+
+try:
+    from urllib.parse import quote
+except ImportError:
+    from urllib import quote
+
+
+def build_uri(secret, name, initial_count=None, issuer_name=None):
+    """
+    Returns the provisioning URI for the OTP; works for either TOTP or HOTP.
+
+    This can then be encoded in a QR Code and used to provision the Google
+    Authenticator app.
+
+    For module-internal use.
+
+    See also:
+        http://code.google.com/p/google-authenticator/wiki/KeyUriFormat
+
+    @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.
+        If none, the OTP type will be assumed as TOTP.
+    @param [String] the name of the OTP issuer; this will be the
+        organization title of the OTP entry in Authenticator
+    @return [String] provisioning uri
+    """
+    # initial_count may be 0 as a valid param
+    is_initial_count_present = (initial_count is not None)
+
+    otp_type = 'hotp' if is_initial_count_present else 'totp'
+    base = 'otpauth://%s/' % otp_type
+
+    if issuer_name:
+        issuer_name = quote(issuer_name)
+        base += '%s:' % issuer_name
+
+    uri = '%(base)s%(name)s?secret=%(secret)s' % {
+        'name': quote(name, safe='@'),
+        'secret': secret,
+        'base': base,
+    }
+
+    if is_initial_count_present:
+        uri += '&counter=%s' % initial_count
+
+    if issuer_name:
+        uri += '&issuer=%s' % issuer_name
+
+    return uri
+
+
+def strings_equal(s1, s2):
+    """
+    Timing-attack resistant string comparison.
+
+    Normal comparison using == will short-circuit on the first mismatching
+    character. This avoids that by scanning the whole string, though we
+    still reveal to a timing attack whether the strings are the same
+    length.
+    """
+    try:
+        # Python 3.3+ and 2.7.7+ include a timing-attack-resistant
+        # comparison function, which is probably more reliable than ours.
+        # Use it if available.
+        from hmac import compare_digest
+
+        return compare_digest(s1, s2)
+    except ImportError:
+        pass
+
+    if len(s1) != len(s2):
+        return False
+
+    differences = 0
+    for c1, c2 in zip(s1, s2):
+        differences |= ord(c1) ^ ord(c2)
+    return differences == 0
diff --git a/test.py b/test.py
new file mode 100755
index 0000000..d36557b
--- /dev/null
+++ b/test.py
@@ -0,0 +1,171 @@
+#!/usr/bin/env python
+
+from __future__ import print_function, unicode_literals, division, absolute_import
+
+import base64
+import datetime
+import hashlib
+import os
+import sys
+import unittest
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
+import pyotp
+
+
+class HOTPExampleValuesFromTheRFC(unittest.TestCase):
+    def testMatchTheRFC(self):
+        # 12345678901234567890 in Bas32
+        # GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ
+        hotp = pyotp.HOTP('GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ')
+        self.assertEqual(hotp.at(0), '755224')
+        self.assertEqual(hotp.at(1), '287082')
+        self.assertEqual(hotp.at(2), '359152')
+        self.assertEqual(hotp.at(3), '969429')
+        self.assertEqual(hotp.at(4), '338314')
+        self.assertEqual(hotp.at(5), '254676')
+        self.assertEqual(hotp.at(6), '287922')
+        self.assertEqual(hotp.at(7), '162583')
+        self.assertEqual(hotp.at(8), '399871')
+        self.assertEqual(hotp.at(9), '520489')
+
+    def testVerifyAnOTPAndNowAllowReuse(self):
+        hotp = pyotp.HOTP('GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ')
+        self.assertTrue(hotp.verify('520489', 9))
+        self.assertFalse(hotp.verify('520489', 10))
+        self.assertFalse(hotp.verify('520489', 10))
+
+    def testProvisioningURI(self):
+        hotp = pyotp.HOTP('wrn3pqx5uqxqvnqr')
+
+        self.assertEqual(
+            hotp.provisioning_uri('mark at percival'),
+            'otpauth://hotp/mark@percival?secret=wrn3pqx5uqxqvnqr&counter=0')
+
+        self.assertEqual(
+            hotp.provisioning_uri('mark at percival', initial_count=12),
+            'otpauth://hotp/mark@percival?secret=wrn3pqx5uqxqvnqr&counter=12')
+
+        self.assertEqual(
+            hotp.provisioning_uri('mark at percival', issuer_name='FooCorp!'),
+            'otpauth://hotp/FooCorp%21:mark@percival?secret=wrn3pqx5uqxqvnqr&counter=0&issuer=FooCorp%21')
+
+
+class TOTPExampleValuesFromTheRFC(unittest.TestCase):
+    RFC_VALUES = {
+        (hashlib.sha1, b'12345678901234567890'): (
+            (59, '94287082'),
+            (1111111109, '07081804'),
+            (1111111111, '14050471'),
+            (1234567890, '89005924'),
+            (2000000000, '69279037'),
+            (20000000000, '65353130'),
+        ),
+
+        (hashlib.sha256, b'12345678901234567890123456789012'): (
+            (59, 46119246),
+            (1111111109, '68084774'),
+            (1111111111, '67062674'),
+            (1234567890, '91819424'),
+            (2000000000, '90698825'),
+            (20000000000, '77737706'),
+        ),
+
+        (hashlib.sha512, b'1234567890123456789012345678901234567890123456789012345678901234'): (
+            (59, 90693936),
+            (1111111109, '25091201'),
+            (1111111111, '99943326'),
+            (1234567890, '93441116'),
+            (2000000000, '38618901'),
+            (20000000000, '47863826'),
+        ),
+    }
+
+    def testMatchTheRFC(self):
+        for digest, secret in self.RFC_VALUES:
+            totp = pyotp.TOTP(base64.b32encode(secret), 8, digest)
+            for utime, code in self.RFC_VALUES[(digest, secret)]:
+                value = totp.at(utime)
+                msg = "%s != %s (%s, time=%d)"
+                msg %= (value, code, digest().name, utime)
+                self.assertEqual(value, str(code), msg)
+
+    def testMatchTheRFCDigitLength(self):
+        totp = pyotp.TOTP('GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ')
+        self.assertEqual(totp.at(1111111111), '050471')
+        self.assertEqual(totp.at(1234567890), '005924')
+        self.assertEqual(totp.at(2000000000), '279037')
+
+    def testMatchTheGoogleAuthenticatorOutput(self):
+        totp = pyotp.TOTP('wrn3pqx5uqxqvnqr')
+        with Timecop(1297553958):
+            self.assertEqual(totp.now(), '102705')
+
+    def testValidateATimeBasedOTP(self):
+        totp = pyotp.TOTP('wrn3pqx5uqxqvnqr')
+        with Timecop(1297553958):
+            self.assertTrue(totp.verify('102705'))
+            self.assertTrue(totp.verify('102705'))
+        with Timecop(1297553958 + 30):
+            self.assertFalse(totp.verify('102705'))
+
+    def testValidateATimeBasedOTPWithDigitLength(self):
+        totp = pyotp.TOTP('GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ')
+        with Timecop(1111111111):
+            self.assertTrue(totp.verify('050471'))
+        with Timecop(1297553958 + 30):
+            self.assertFalse(totp.verify('050471'))
+
+    def testProvisioningURI(self):
+        totp = pyotp.TOTP('wrn3pqx5uqxqvnqr')
+        self.assertEqual(
+            totp.provisioning_uri('mark at percival'),
+            'otpauth://totp/mark@percival?secret=wrn3pqx5uqxqvnqr')
+
+        self.assertEqual(
+            totp.provisioning_uri('mark at percival', issuer_name='FooCorp!'),
+            'otpauth://totp/FooCorp%21:mark@percival?secret=wrn3pqx5uqxqvnqr&issuer=FooCorp%21')
+
+    def testRandomKeyGeneration(self):
+        self.assertEqual(len(pyotp.random_base32()), 16)
+        self.assertEqual(len(pyotp.random_base32(length=20)), 20)
+
+
+class StringComparisonTest(unittest.TestCase):
+    def testComparisons(self):
+        self.assertTrue(pyotp.utils.strings_equal("", ""))
+        self.assertTrue(pyotp.utils.strings_equal("a", "a"))
+        self.assertTrue(pyotp.utils.strings_equal("a" * 1000, "a" * 1000))
+
+        self.assertFalse(pyotp.utils.strings_equal("", "a"))
+        self.assertFalse(pyotp.utils.strings_equal("a", ""))
+        self.assertFalse(pyotp.utils.strings_equal("a" * 999 + "b", "a" * 1000))
+
+
+class Timecop(object):
+    """
+    Half-assed clone of timecop.rb, just enough to pass our tests.
+    """
+
+    def __init__(self, freeze_timestamp):
+        self.freeze_timestamp = freeze_timestamp
+
... 19 lines suppressed ...

-- 
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