summaryrefslogtreecommitdiffstats
path: root/module/lib/beaker/crypto/pbkdf2.py
diff options
context:
space:
mode:
Diffstat (limited to 'module/lib/beaker/crypto/pbkdf2.py')
-rw-r--r--module/lib/beaker/crypto/pbkdf2.py342
1 files changed, 0 insertions, 342 deletions
diff --git a/module/lib/beaker/crypto/pbkdf2.py b/module/lib/beaker/crypto/pbkdf2.py
deleted file mode 100644
index 96dc5fbb2..000000000
--- a/module/lib/beaker/crypto/pbkdf2.py
+++ /dev/null
@@ -1,342 +0,0 @@
-#!/usr/bin/python
-# -*- coding: ascii -*-
-###########################################################################
-# PBKDF2.py - PKCS#5 v2.0 Password-Based Key Derivation
-#
-# Copyright (C) 2007 Dwayne C. Litzenberger <dlitz@dlitz.net>
-# All rights reserved.
-#
-# Permission to use, copy, modify, and distribute this software and its
-# documentation for any purpose and without fee is hereby granted,
-# provided that the above copyright notice appear in all copies and that
-# both that copyright notice and this permission notice appear in
-# supporting documentation.
-#
-# THE AUTHOR PROVIDES THIS SOFTWARE ``AS IS'' AND ANY EXPRESSED OR
-# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-# Country of origin: Canada
-#
-###########################################################################
-# Sample PBKDF2 usage:
-# from Crypto.Cipher import AES
-# from PBKDF2 import PBKDF2
-# import os
-#
-# salt = os.urandom(8) # 64-bit salt
-# key = PBKDF2("This passphrase is a secret.", salt).read(32) # 256-bit key
-# iv = os.urandom(16) # 128-bit IV
-# cipher = AES.new(key, AES.MODE_CBC, iv)
-# ...
-#
-# Sample crypt() usage:
-# from PBKDF2 import crypt
-# pwhash = crypt("secret")
-# alleged_pw = raw_input("Enter password: ")
-# if pwhash == crypt(alleged_pw, pwhash):
-# print "Password good"
-# else:
-# print "Invalid password"
-#
-###########################################################################
-# History:
-#
-# 2007-07-27 Dwayne C. Litzenberger <dlitz@dlitz.net>
-# - Initial Release (v1.0)
-#
-# 2007-07-31 Dwayne C. Litzenberger <dlitz@dlitz.net>
-# - Bugfix release (v1.1)
-# - SECURITY: The PyCrypto XOR cipher (used, if available, in the _strxor
-# function in the previous release) silently truncates all keys to 64
-# bytes. The way it was used in the previous release, this would only be
-# problem if the pseudorandom function that returned values larger than
-# 64 bytes (so SHA1, SHA256 and SHA512 are fine), but I don't like
-# anything that silently reduces the security margin from what is
-# expected.
-#
-###########################################################################
-
-__version__ = "1.1"
-
-from struct import pack
-from binascii import b2a_hex
-from random import randint
-
-from base64 import b64encode
-
-from beaker.crypto.util import hmac as HMAC, hmac_sha1 as SHA1
-
-def strxor(a, b):
- return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b)])
-
-class PBKDF2(object):
- """PBKDF2.py : PKCS#5 v2.0 Password-Based Key Derivation
-
- This implementation takes a passphrase and a salt (and optionally an
- iteration count, a digest module, and a MAC module) and provides a
- file-like object from which an arbitrarily-sized key can be read.
-
- If the passphrase and/or salt are unicode objects, they are encoded as
- UTF-8 before they are processed.
-
- The idea behind PBKDF2 is to derive a cryptographic key from a
- passphrase and a salt.
-
- PBKDF2 may also be used as a strong salted password hash. The
- 'crypt' function is provided for that purpose.
-
- Remember: Keys generated using PBKDF2 are only as strong as the
- passphrases they are derived from.
- """
-
- def __init__(self, passphrase, salt, iterations=1000,
- digestmodule=SHA1, macmodule=HMAC):
- if not callable(macmodule):
- macmodule = macmodule.new
- self.__macmodule = macmodule
- self.__digestmodule = digestmodule
- self._setup(passphrase, salt, iterations, self._pseudorandom)
-
- def _pseudorandom(self, key, msg):
- """Pseudorandom function. e.g. HMAC-SHA1"""
- return self.__macmodule(key=key, msg=msg,
- digestmod=self.__digestmodule).digest()
-
- def read(self, bytes):
- """Read the specified number of key bytes."""
- if self.closed:
- raise ValueError("file-like object is closed")
-
- size = len(self.__buf)
- blocks = [self.__buf]
- i = self.__blockNum
- while size < bytes:
- i += 1
- if i > 0xffffffff:
- # We could return "" here, but
- raise OverflowError("derived key too long")
- block = self.__f(i)
- blocks.append(block)
- size += len(block)
- buf = "".join(blocks)
- retval = buf[:bytes]
- self.__buf = buf[bytes:]
- self.__blockNum = i
- return retval
-
- def __f(self, i):
- # i must fit within 32 bits
- assert (1 <= i <= 0xffffffff)
- U = self.__prf(self.__passphrase, self.__salt + pack("!L", i))
- result = U
- for j in xrange(2, 1+self.__iterations):
- U = self.__prf(self.__passphrase, U)
- result = strxor(result, U)
- return result
-
- def hexread(self, octets):
- """Read the specified number of octets. Return them as hexadecimal.
-
- Note that len(obj.hexread(n)) == 2*n.
- """
- return b2a_hex(self.read(octets))
-
- def _setup(self, passphrase, salt, iterations, prf):
- # Sanity checks:
-
- # passphrase and salt must be str or unicode (in the latter
- # case, we convert to UTF-8)
- if isinstance(passphrase, unicode):
- passphrase = passphrase.encode("UTF-8")
- if not isinstance(passphrase, str):
- raise TypeError("passphrase must be str or unicode")
- if isinstance(salt, unicode):
- salt = salt.encode("UTF-8")
- if not isinstance(salt, str):
- raise TypeError("salt must be str or unicode")
-
- # iterations must be an integer >= 1
- if not isinstance(iterations, (int, long)):
- raise TypeError("iterations must be an integer")
- if iterations < 1:
- raise ValueError("iterations must be at least 1")
-
- # prf must be callable
- if not callable(prf):
- raise TypeError("prf must be callable")
-
- self.__passphrase = passphrase
- self.__salt = salt
- self.__iterations = iterations
- self.__prf = prf
- self.__blockNum = 0
- self.__buf = ""
- self.closed = False
-
- def close(self):
- """Close the stream."""
- if not self.closed:
- del self.__passphrase
- del self.__salt
- del self.__iterations
- del self.__prf
- del self.__blockNum
- del self.__buf
- self.closed = True
-
-def crypt(word, salt=None, iterations=None):
- """PBKDF2-based unix crypt(3) replacement.
-
- The number of iterations specified in the salt overrides the 'iterations'
- parameter.
-
- The effective hash length is 192 bits.
- """
-
- # Generate a (pseudo-)random salt if the user hasn't provided one.
- if salt is None:
- salt = _makesalt()
-
- # salt must be a string or the us-ascii subset of unicode
- if isinstance(salt, unicode):
- salt = salt.encode("us-ascii")
- if not isinstance(salt, str):
- raise TypeError("salt must be a string")
-
- # word must be a string or unicode (in the latter case, we convert to UTF-8)
- if isinstance(word, unicode):
- word = word.encode("UTF-8")
- if not isinstance(word, str):
- raise TypeError("word must be a string or unicode")
-
- # Try to extract the real salt and iteration count from the salt
- if salt.startswith("$p5k2$"):
- (iterations, salt, dummy) = salt.split("$")[2:5]
- if iterations == "":
- iterations = 400
- else:
- converted = int(iterations, 16)
- if iterations != "%x" % converted: # lowercase hex, minimum digits
- raise ValueError("Invalid salt")
- iterations = converted
- if not (iterations >= 1):
- raise ValueError("Invalid salt")
-
- # Make sure the salt matches the allowed character set
- allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
- for ch in salt:
- if ch not in allowed:
- raise ValueError("Illegal character %r in salt" % (ch,))
-
- if iterations is None or iterations == 400:
- iterations = 400
- salt = "$p5k2$$" + salt
- else:
- salt = "$p5k2$%x$%s" % (iterations, salt)
- rawhash = PBKDF2(word, salt, iterations).read(24)
- return salt + "$" + b64encode(rawhash, "./")
-
-# Add crypt as a static method of the PBKDF2 class
-# This makes it easier to do "from PBKDF2 import PBKDF2" and still use
-# crypt.
-PBKDF2.crypt = staticmethod(crypt)
-
-def _makesalt():
- """Return a 48-bit pseudorandom salt for crypt().
-
- This function is not suitable for generating cryptographic secrets.
- """
- binarysalt = "".join([pack("@H", randint(0, 0xffff)) for i in range(3)])
- return b64encode(binarysalt, "./")
-
-def test_pbkdf2():
- """Module self-test"""
- from binascii import a2b_hex
-
- #
- # Test vectors from RFC 3962
- #
-
- # Test 1
- result = PBKDF2("password", "ATHENA.MIT.EDUraeburn", 1).read(16)
- expected = a2b_hex("cdedb5281bb2f801565a1122b2563515")
- if result != expected:
- raise RuntimeError("self-test failed")
-
- # Test 2
- result = PBKDF2("password", "ATHENA.MIT.EDUraeburn", 1200).hexread(32)
- expected = ("5c08eb61fdf71e4e4ec3cf6ba1f5512b"
- "a7e52ddbc5e5142f708a31e2e62b1e13")
- if result != expected:
- raise RuntimeError("self-test failed")
-
- # Test 3
- result = PBKDF2("X"*64, "pass phrase equals block size", 1200).hexread(32)
- expected = ("139c30c0966bc32ba55fdbf212530ac9"
- "c5ec59f1a452f5cc9ad940fea0598ed1")
- if result != expected:
- raise RuntimeError("self-test failed")
-
- # Test 4
- result = PBKDF2("X"*65, "pass phrase exceeds block size", 1200).hexread(32)
- expected = ("9ccad6d468770cd51b10e6a68721be61"
- "1a8b4d282601db3b36be9246915ec82a")
- if result != expected:
- raise RuntimeError("self-test failed")
-
- #
- # Other test vectors
- #
-
- # Chunked read
- f = PBKDF2("kickstart", "workbench", 256)
- result = f.read(17)
- result += f.read(17)
- result += f.read(1)
- result += f.read(2)
- result += f.read(3)
- expected = PBKDF2("kickstart", "workbench", 256).read(40)
- if result != expected:
- raise RuntimeError("self-test failed")
-
- #
- # crypt() test vectors
- #
-
- # crypt 1
- result = crypt("cloadm", "exec")
- expected = '$p5k2$$exec$r1EWMCMk7Rlv3L/RNcFXviDefYa0hlql'
- if result != expected:
- raise RuntimeError("self-test failed")
-
- # crypt 2
- result = crypt("gnu", '$p5k2$c$u9HvcT4d$.....')
- expected = '$p5k2$c$u9HvcT4d$Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g'
- if result != expected:
- raise RuntimeError("self-test failed")
-
- # crypt 3
- result = crypt("dcl", "tUsch7fU", iterations=13)
- expected = "$p5k2$d$tUsch7fU$nqDkaxMDOFBeJsTSfABsyn.PYUXilHwL"
- if result != expected:
- raise RuntimeError("self-test failed")
-
- # crypt 4 (unicode)
- result = crypt(u'\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2',
- '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ')
- expected = '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ'
- if result != expected:
- raise RuntimeError("self-test failed")
-
-if __name__ == '__main__':
- test_pbkdf2()
-
-# vim:set ts=4 sw=4 sts=4 expandtab: