aboutsummaryrefslogtreecommitdiffstats
path: root/cryptography
diff options
context:
space:
mode:
authorPaul Kehrer <paul.l.kehrer@gmail.com>2014-02-20 22:11:56 -0600
committerPaul Kehrer <paul.l.kehrer@gmail.com>2014-02-20 22:11:56 -0600
commit3c0de81384cf5a176a412aac3663ed054ea446da (patch)
treea02e32e6fed266877693d281a032d040ff8e3ad6 /cryptography
parentd2f24580aa9e5f90c1011c2cfc7720077b74cd4d (diff)
parentc2e53409242f750f7966b5d1e45f62f9c818b07d (diff)
downloadcryptography-3c0de81384cf5a176a412aac3663ed054ea446da.tar.gz
cryptography-3c0de81384cf5a176a412aac3663ed054ea446da.tar.bz2
cryptography-3c0de81384cf5a176a412aac3663ed054ea446da.zip
Merge pull request #598 from Ayrx/hotp-impl
HOTP Implementation
Diffstat (limited to 'cryptography')
-rw-r--r--cryptography/exceptions.py4
-rw-r--r--cryptography/hazmat/primitives/twofactor/__init__.py0
-rw-r--r--cryptography/hazmat/primitives/twofactor/hotp.py54
3 files changed, 58 insertions, 0 deletions
diff --git a/cryptography/exceptions.py b/cryptography/exceptions.py
index e2542a1f..f9849e2f 100644
--- a/cryptography/exceptions.py
+++ b/cryptography/exceptions.py
@@ -42,3 +42,7 @@ class InternalError(Exception):
class InvalidKey(Exception):
pass
+
+
+class InvalidToken(Exception):
+ pass
diff --git a/cryptography/hazmat/primitives/twofactor/__init__.py b/cryptography/hazmat/primitives/twofactor/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/cryptography/hazmat/primitives/twofactor/__init__.py
diff --git a/cryptography/hazmat/primitives/twofactor/hotp.py b/cryptography/hazmat/primitives/twofactor/hotp.py
new file mode 100644
index 00000000..0bc4cc56
--- /dev/null
+++ b/cryptography/hazmat/primitives/twofactor/hotp.py
@@ -0,0 +1,54 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import struct
+
+import six
+
+from cryptography.exceptions import InvalidToken
+from cryptography.hazmat.primitives import constant_time, hmac
+from cryptography.hazmat.primitives.hashes import SHA1
+
+
+class HOTP(object):
+ def __init__(self, key, length, backend):
+
+ if len(key) < 16:
+ raise ValueError("Key length has to be at least 128 bits.")
+
+ if length < 6 or length > 8:
+ raise ValueError("Length of HOTP has to be between 6 to 8.")
+
+ self._key = key
+ self._length = length
+ self._backend = backend
+
+ def generate(self, counter):
+ truncated_value = self._dynamic_truncate(counter)
+ hotp = truncated_value % (10**self._length)
+ return "{0:0{1}}".format(hotp, self._length).encode()
+
+ def verify(self, hotp, counter):
+ if not constant_time.bytes_eq(self.generate(counter), hotp):
+ raise InvalidToken("Supplied HOTP value does not match")
+
+ def _dynamic_truncate(self, counter):
+ ctx = hmac.HMAC(self._key, SHA1(), self._backend)
+ ctx.update(struct.pack(">Q", counter))
+ hmac_value = ctx.finalize()
+
+ offset_bits = six.indexbytes(hmac_value, 19) & 0b1111
+
+ offset = int(offset_bits)
+ P = hmac_value[offset:offset+4]
+ return struct.unpack(">I", P)[0] & 0x7fffffff