Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(73)

Side by Side Diff: third_party/google-endpoints/jwkest/PBKDF2.py

Issue 2666783008: Add google-endpoints to third_party/. (Closed)
Patch Set: Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #!/usr/bin/python
2 # -*- coding: ascii -*-
3 ###########################################################################
4 # PBKDF2.py - PKCS#5 v2.0 Password-Based Key Derivation
5 #
6 # Copyright (C) 2007, 2008 Dwayne C. Litzenberger <dlitz@dlitz.net>
7 # All rights reserved.
8 #
9 # Permission to use, copy, modify, and distribute this software and its
10 # documentation for any purpose and without fee is hereby granted,
11 # provided that the above copyright notice appear in all copies and that
12 # both that copyright notice and this permission notice appear in
13 # supporting documentation.
14 #
15 # THE AUTHOR PROVIDES THIS SOFTWARE ``AS IS'' AND ANY EXPRESSED OR
16 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 #
26 # Country of origin: Canada
27 #
28 ###########################################################################
29 # Sample PBKDF2 usage:
30 # from Crypto.Cipher import AES
31 # from PBKDF2 import PBKDF2
32 # import os
33 #
34 # salt = os.urandom(8) # 64-bit salt
35 # key = PBKDF2("This passphrase is a secret.", salt).read(32) # 256-bit key
36 # iv = os.urandom(16) # 128-bit IV
37 # cipher = AES.new(key, AES.MODE_CBC, iv)
38 # ...
39 #
40 # Sample crypt() usage:
41 # from PBKDF2 import crypt
42 # pwhash = crypt("secret")
43 # alleged_pw = raw_input("Enter password: ")
44 # if pwhash == crypt(alleged_pw, pwhash):
45 # print "Password good"
46 # else:
47 # print "Invalid password"
48 #
49 ###########################################################################
50 # History:
51 #
52 # 2007-07-27 Dwayne C. Litzenberger <dlitz@dlitz.net>
53 # - Initial Release (v1.0)
54 #
55 # 2007-07-31 Dwayne C. Litzenberger <dlitz@dlitz.net>
56 # - Bugfix release (v1.1)
57 # - SECURITY: The PyCrypto XOR cipher (used, if available, in the _strxor
58 # function in the previous release) silently truncates all keys to 64
59 # bytes. The way it was used in the previous release, this would only be
60 # problem if the pseudorandom function that returned values larger than
61 # 64 bytes (so SHA1, SHA256 and SHA512 are fine), but I don't like
62 # anything that silently reduces the security margin from what is
63 # expected.
64 #
65 # 2008-06-17 Dwayne C. Litzenberger <dlitz@dlitz.net>
66 # - Compatibility release (v1.2)
67 # - Add support for older versions of Python (2.2 and 2.3).
68 #
69 ###########################################################################
70
71 __version__ = "1.2"
72
73 from builtins import chr
74 from builtins import zip
75 from builtins import range
76 from builtins import object
77
78 import string
79 from struct import pack
80 from binascii import b2a_hex
81 from random import randint
82
83 try:
84 # Use PyCrypto (if available)
85 from Crypto.Hash import HMAC, SHA as SHA1
86
87 except ImportError:
88 # PyCrypto not available. Use the Python standard library.
89 import hmac as HMAC
90 import sha as SHA1
91
92 def strxor(a, b):
93 return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b)])
94
95 def b64encode(data, chars="+/"):
96 tt = string.maketrans("+/", chars)
97 return data.encode('base64').replace("\n", "").translate(tt)
98
99 class PBKDF2(object):
100 """PBKDF2.py : PKCS#5 v2.0 Password-Based Key Derivation
101
102 This implementation takes a passphrase and a salt (and optionally an
103 iteration count, a digest module, and a MAC module) and provides a
104 file-like object from which an arbitrarily-sized key can be read.
105
106 If the passphrase and/or salt are unicode objects, they are encoded as
107 UTF-8 before they are processed.
108
109 The idea behind PBKDF2 is to derive a cryptographic key from a
110 passphrase and a salt.
111
112 PBKDF2 may also be used as a strong salted password hash. The
113 'crypt' function is provided for that purpose.
114
115 Remember: Keys generated using PBKDF2 are only as strong as the
116 passphrases they are derived from.
117 """
118
119 def __init__(self, passphrase, salt, iterations=1000,
120 digestmodule=SHA1, macmodule=HMAC):
121 self.__macmodule = macmodule
122 self.__digestmodule = digestmodule
123 self._setup(passphrase, salt, iterations, self._pseudorandom)
124
125 def _pseudorandom(self, key, msg):
126 """Pseudorandom function. e.g. HMAC-SHA1"""
127 return self.__macmodule.new(key=key, msg=msg,
128 digestmod=self.__digestmodule).digest()
129
130 def read(self, bytes):
131 """Read the specified number of key bytes."""
132 if self.closed:
133 raise ValueError("file-like object is closed")
134
135 size = len(self.__buf)
136 blocks = [self.__buf]
137 i = self.__blockNum
138 while size < bytes:
139 i += 1
140 if i > 0xffffffff or i < 1:
141 # We could return "" here, but
142 raise OverflowError("derived key too long")
143 block = self.__f(i)
144 blocks.append(block)
145 size += len(block)
146 buf = "".join(blocks)
147 retval = buf[:bytes]
148 self.__buf = buf[bytes:]
149 self.__blockNum = i
150 return retval
151
152 def __f(self, i):
153 # i must fit within 32 bits
154 assert 1 <= i <= 0xffffffff
155 U = self.__prf(self.__passphrase, self.__salt + pack("!L", i))
156 result = U
157 for j in range(2, 1+self.__iterations):
158 U = self.__prf(self.__passphrase, U)
159 result = strxor(result, U)
160 return result
161
162 def hexread(self, octets):
163 """Read the specified number of octets. Return them as hexadecimal.
164
165 Note that len(obj.hexread(n)) == 2*n.
166 """
167 return b2a_hex(self.read(octets))
168
169 def _setup(self, passphrase, salt, iterations, prf):
170 # Sanity checks:
171
172 # passphrase and salt must be str or unicode (in the latter
173 # case, we convert to UTF-8)
174 if isinstance(passphrase, str):
175 passphrase = passphrase.encode("UTF-8")
176 if not isinstance(passphrase, str):
177 raise TypeError("passphrase must be str or unicode")
178 if isinstance(salt, str):
179 salt = salt.encode("UTF-8")
180 if not isinstance(salt, str):
181 raise TypeError("salt must be str or unicode")
182
183 # iterations must be an integer >= 1
184 if not isinstance(iterations, (int, int)):
185 raise TypeError("iterations must be an integer")
186 if iterations < 1:
187 raise ValueError("iterations must be at least 1")
188
189 # prf must be callable
190 if not callable(prf):
191 raise TypeError("prf must be callable")
192
193 self.__passphrase = passphrase
194 self.__salt = salt
195 self.__iterations = iterations
196 self.__prf = prf
197 self.__blockNum = 0
198 self.__buf = ""
199 self.closed = False
200
201 def close(self):
202 """Close the stream."""
203 if not self.closed:
204 del self.__passphrase
205 del self.__salt
206 del self.__iterations
207 del self.__prf
208 del self.__blockNum
209 del self.__buf
210 self.closed = True
211
212 def crypt(word, salt=None, iterations=None):
213 """PBKDF2-based unix crypt(3) replacement.
214
215 The number of iterations specified in the salt overrides the 'iterations'
216 parameter.
217
218 The effective hash length is 192 bits.
219 """
220
221 # Generate a (pseudo-)random salt if the user hasn't provided one.
222 if salt is None:
223 salt = _makesalt()
224
225 # salt must be a string or the us-ascii subset of unicode
226 if isinstance(salt, str):
227 salt = salt.encode("us-ascii")
228 if not isinstance(salt, str):
229 raise TypeError("salt must be a string")
230
231 # word must be a string or unicode (in the latter case, we convert to UTF-8)
232 if isinstance(word, str):
233 word = word.encode("UTF-8")
234 if not isinstance(word, str):
235 raise TypeError("word must be a string or unicode")
236
237 # Try to extract the real salt and iteration count from the salt
238 if salt.startswith("$p5k2$"):
239 (iterations, salt, dummy) = salt.split("$")[2:5]
240 if iterations == "":
241 iterations = 400
242 else:
243 converted = int(iterations, 16)
244 if iterations != "%x" % converted: # lowercase hex, minimum digits
245 raise ValueError("Invalid salt")
246 iterations = converted
247 if not (iterations >= 1):
248 raise ValueError("Invalid salt")
249
250 # Make sure the salt matches the allowed character set
251 allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
252 for ch in salt:
253 if ch not in allowed:
254 raise ValueError("Illegal character %r in salt" % (ch,))
255
256 if iterations is None or iterations == 400:
257 iterations = 400
258 salt = "$p5k2$$" + salt
259 else:
260 salt = "$p5k2$%x$%s" % (iterations, salt)
261 rawhash = PBKDF2(word, salt, iterations).read(24)
262 return salt + "$" + b64encode(rawhash, "./")
263
264 # Add crypt as a static method of the PBKDF2 class
265 # This makes it easier to do "from PBKDF2 import PBKDF2" and still use
266 # crypt.
267 PBKDF2.crypt = staticmethod(crypt)
268
269 def _makesalt():
270 """Return a 48-bit pseudorandom salt for crypt().
271
272 This function is not suitable for generating cryptographic secrets.
273 """
274 binarysalt = "".join([pack("@H", randint(0, 0xffff)) for i in range(3)])
275 return b64encode(binarysalt, "./")
276
277 def test_pbkdf2():
278 """Module self-test"""
279 from binascii import a2b_hex
280
281 #
282 # Test vectors from RFC 3962
283 #
284
285 # Test 1
286 result = PBKDF2("password", "ATHENA.MIT.EDUraeburn", 1).read(16)
287 expected = a2b_hex("cdedb5281bb2f801565a1122b2563515")
288 if result != expected:
289 raise RuntimeError("self-test failed")
290
291 # Test 2
292 result = PBKDF2("password", "ATHENA.MIT.EDUraeburn", 1200).hexread(32)
293 expected = ("5c08eb61fdf71e4e4ec3cf6ba1f5512b"
294 "a7e52ddbc5e5142f708a31e2e62b1e13")
295 if result != expected:
296 raise RuntimeError("self-test failed")
297
298 # Test 3
299 result = PBKDF2("X"*64, "pass phrase equals block size", 1200).hexread(32)
300 expected = ("139c30c0966bc32ba55fdbf212530ac9"
301 "c5ec59f1a452f5cc9ad940fea0598ed1")
302 if result != expected:
303 raise RuntimeError("self-test failed")
304
305 # Test 4
306 result = PBKDF2("X"*65, "pass phrase exceeds block size", 1200).hexread(32)
307 expected = ("9ccad6d468770cd51b10e6a68721be61"
308 "1a8b4d282601db3b36be9246915ec82a")
309 if result != expected:
310 raise RuntimeError("self-test failed")
311
312 #
313 # Other test vectors
314 #
315
316 # Chunked read
317 f = PBKDF2("kickstart", "workbench", 256)
318 result = f.read(17)
319 result += f.read(17)
320 result += f.read(1)
321 result += f.read(2)
322 result += f.read(3)
323 expected = PBKDF2("kickstart", "workbench", 256).read(40)
324 if result != expected:
325 raise RuntimeError("self-test failed")
326
327 #
328 # crypt() test vectors
329 #
330
331 # crypt 1
332 result = crypt("cloadm", "exec")
333 expected = '$p5k2$$exec$r1EWMCMk7Rlv3L/RNcFXviDefYa0hlql'
334 if result != expected:
335 raise RuntimeError("self-test failed")
336
337 # crypt 2
338 result = crypt("gnu", '$p5k2$c$u9HvcT4d$.....')
339 expected = '$p5k2$c$u9HvcT4d$Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g'
340 if result != expected:
341 raise RuntimeError("self-test failed")
342
343 # crypt 3
344 result = crypt("dcl", "tUsch7fU", iterations=13)
345 expected = "$p5k2$d$tUsch7fU$nqDkaxMDOFBeJsTSfABsyn.PYUXilHwL"
346 if result != expected:
347 raise RuntimeError("self-test failed")
348
349 # crypt 4 (unicode)
350 result = crypt(u'\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2',
351 '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ')
352 expected = '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ'
353 if result != expected:
354 raise RuntimeError("self-test failed")
355
356 if __name__ == '__main__':
357 test_pbkdf2()
358
359 # vim:set ts=4 sw=4 sts=4 expandtab:
OLDNEW
« no previous file with comments | « third_party/google-endpoints/httplib2/socks.py ('k') | third_party/google-endpoints/jwkest/__init__.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698