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