3434_bempty = b""
3535_bjoin = _bempty .join
3636
37+
3738def bchr (c ):
3839 if bytes == str :
3940 # Python 2.
@@ -42,6 +43,7 @@ def bchr(c):
4243 # Python 3.
4344 return bytes ((c ,))
4445
46+
4547def bord (c ):
4648 if bytes == str :
4749 # Python 2.
@@ -50,12 +52,14 @@ def bord(c):
5052 # Python 3. Elements of bytes are integers.
5153 return c
5254
53- #NOTE: PBKDF2 hash is using this variant of base64 to minimize encoding size,
55+
56+ # NOTE: PBKDF2 hash is using this variant of base64 to minimize encoding size,
5457# and have charset that's compatible w/ unix crypt variants
5558def h64encode (data ):
5659 """encode using variant of base64"""
5760 return b2s (b64encode (data , b"./" ).strip (b"=\n " ))
5861
62+
5963def h64decode (data ):
6064 """decode using variant of base64"""
6165 data = s2b (data )
@@ -69,12 +73,14 @@ def h64decode(data):
6973 else :
7074 return b64decode (data + b"=" , b"./" )
7175
76+
7277try :
7378 from hashlib import pbkdf2_hmac
79+
7480 def _pbkdf2 (password , salt , rounds , keylen ):
7581 return pbkdf2_hmac ('sha1' , password , salt , rounds , keylen )
7682except ImportError :
77- #no hashlib.pbkdf2_hmac - make our own pbkdf2 function
83+ # no hashlib.pbkdf2_hmac - make our own pbkdf2 function
7884 from struct import pack
7985 from hmac import HMAC
8086
@@ -83,37 +89,40 @@ def xor_bytes(left, right):
8389 return _bjoin (bchr (bord (l ) ^ bord (r )) for l , r in zip (left , right ))
8490
8591 def _pbkdf2 (password , salt , rounds , keylen ):
86- digest_size = 20 # sha1 generates 20-byte blocks
92+ digest_size = 20 # sha1 generates 20-byte blocks
8793 total_blocks = int ((keylen + digest_size - 1 )/ digest_size )
8894 hmac_template = HMAC (password , None , sha1 )
8995 out = _bempty
9096 for i in range (1 , total_blocks + 1 ):
9197 hmac = hmac_template .copy ()
92- hmac .update (salt + pack (">L" ,i ))
98+ hmac .update (salt + pack (">L" , i ))
9399 block = tmp = hmac .digest ()
94- for j in range (rounds - 1 ):
100+ for _j in range (rounds - 1 ):
95101 hmac = hmac_template .copy ()
96102 hmac .update (tmp )
97103 tmp = hmac .digest ()
98- #TODO: need to speed up this call
104+ # TODO: need to speed up this call
99105 block = xor_bytes (block , tmp )
100106 out += block
101107 return out [:keylen ]
102108
109+
103110def ssha (password , salt ):
104111 ''' Make ssha digest from password and salt.
105112 Based on code of Roberto Aguilar <[email protected] > 106113 https://gist.github.com/rca/7217540
107114 '''
108115 shaval = sha1 (password ) # nosec
109- shaval .update ( salt )
110- ssha_digest = b64encode ( shaval .digest () + salt ).strip ()
116+ shaval .update (salt )
117+ ssha_digest = b64encode (shaval .digest () + salt ).strip ()
111118 return ssha_digest
112119
120+
113121def pbkdf2 (password , salt , rounds , keylen ):
114122 """pkcs#5 password-based key derivation v2.0
115123
116- :arg password: passphrase to use to generate key (if unicode, converted to utf-8)
124+ :arg password: passphrase to use to generate key (if unicode,
125+ converted to utf-8)
117126 :arg salt: salt bytes to use when generating key
118127 :param rounds: number of rounds to use to generate key
119128 :arg keylen: number of bytes to generate
@@ -125,18 +134,20 @@ def pbkdf2(password, salt, rounds, keylen):
125134 """
126135 password = s2b (us2s (password ))
127136 if keylen > 40 :
128- #NOTE: pbkdf2 allows up to (2**31-1)*20 bytes,
137+ # NOTE: pbkdf2 allows up to (2**31-1)*20 bytes,
129138 # but m2crypto has issues on some platforms above 40,
130139 # and such sizes aren't needed for a password hash anyways...
131140 raise ValueError ("key length too large" )
132141 if rounds < 1 :
133142 raise ValueError ("rounds must be positive number" )
134143 return _pbkdf2 (password , salt , rounds , keylen )
135144
145+
136146class PasswordValueError (ValueError ):
137147 """ The password value is not valid """
138148 pass
139149
150+
140151def pbkdf2_unpack (pbkdf2 ):
141152 """ unpack pbkdf2 encrypted password into parts,
142153 assume it has format "{rounds}${salt}${digest}
@@ -145,7 +156,8 @@ def pbkdf2_unpack(pbkdf2):
145156 try :
146157 rounds , salt , digest = pbkdf2 .split ("$" )
147158 except ValueError :
148- raise PasswordValueError ("invalid PBKDF2 hash (wrong number of separators)" )
159+ raise PasswordValueError ("invalid PBKDF2 hash (wrong number of "
160+ "separators)" )
149161 if rounds .startswith ("0" ):
150162 raise PasswordValueError ("invalid PBKDF2 hash (zero-padded rounds)" )
151163 try :
@@ -155,6 +167,7 @@ def pbkdf2_unpack(pbkdf2):
155167 raw_salt = h64decode (salt )
156168 return rounds , salt , raw_salt , digest
157169
170+
158171def encodePassword (plaintext , scheme , other = None , config = None ):
159172 """Encrypt the plaintext password.
160173 """
@@ -179,7 +192,7 @@ def encodePassword(plaintext, scheme, other=None, config=None):
179192 raw_other = b64decode (other )
180193 salt = raw_other [20 :]
181194 else :
182- #new password
195+ # new password
183196 # variable salt length
184197 salt_len = random_ .randbelow (52 - 36 ) + 36
185198 salt = random_ .token_bytes (salt_len )
@@ -198,9 +211,10 @@ def encodePassword(plaintext, scheme, other=None, config=None):
198211 elif scheme == 'plaintext' :
199212 s = plaintext
200213 else :
201- raise PasswordValueError ('Unknown encryption scheme %r' % scheme )
214+ raise PasswordValueError ('Unknown encryption scheme %r' % scheme )
202215 return s
203216
217+
204218def generatePassword (length = 12 ):
205219 chars = string .ascii_letters + string .digits
206220 password = [random_ .choice (chars ) for x in range (length - 1 )]
@@ -209,6 +223,7 @@ def generatePassword(length=12):
209223 password [digitidx :digitidx ] = [random_ .choice (string .digits )]
210224 return '' .join (password )
211225
226+
212227class JournalPassword :
213228 """ Password dummy instance intended for journal operation.
214229 We do not store passwords in the journal any longer. The dummy
@@ -218,7 +233,7 @@ class JournalPassword:
218233 default_scheme = 'PBKDF2' # new encryptions use this scheme
219234 pwre = re .compile (r'{(\w+)}(.+)' )
220235
221- def __init__ (self , encrypted = '' ):
236+ def __init__ (self , encrypted = '' ):
222237 if isinstance (encrypted , self .__class__ ):
223238 self .scheme = encrypted .scheme or self .default_scheme
224239 else :
@@ -249,11 +264,12 @@ def __eq__(self, other):
249264 if self .password is None :
250265 raise ValueError ('Password not set' )
251266 return self .password == encodePassword (other , self .scheme ,
252- self .password or None )
267+ self .password or None )
253268
254269 def __ne__ (self , other ):
255270 return not self .__eq__ (other )
256271
272+
257273class Password (JournalPassword ):
258274 """The class encapsulates a Password property type value in the database.
259275
@@ -276,17 +292,18 @@ class Password(JournalPassword):
276292 >>> 'not sekrit' != p
277293 1
278294 """
279- #TODO: code to migrate from old password schemes.
295+ # TODO: code to migrate from old password schemes.
280296
281297 deprecated_schemes = ["SHA" , "MD5" , "crypt" , "plaintext" ]
282298 known_schemes = ["PBKDF2" , "SSHA" ] + deprecated_schemes
283299
284- def __init__ (self , plaintext = None , scheme = None , encrypted = None , strict = False , config = None ):
300+ def __init__ (self , plaintext = None , scheme = None , encrypted = None ,
301+ strict = False , config = None ):
285302 """Call setPassword if plaintext is not None."""
286303 if scheme is None :
287304 scheme = self .default_scheme
288305 if plaintext is not None :
289- self .setPassword (plaintext , scheme , config = config )
306+ self .setPassword (plaintext , scheme , config = config )
290307 elif encrypted is not None :
291308 self .unpack (encrypted , scheme , strict = strict , config = config )
292309 else :
@@ -321,7 +338,8 @@ def unpack(self, encrypted, scheme=None, strict=False, config=None):
321338 # currently plaintext - encrypt
322339 self .setPassword (encrypted , scheme , config = config )
323340 if strict and self .scheme not in self .known_schemes :
324- raise PasswordValueError ("Unknown encryption scheme: %r" % (self .scheme ,))
341+ raise PasswordValueError ("Unknown encryption scheme: %r" %
342+ (self .scheme ,))
325343
326344 def setPassword (self , plaintext , scheme = None , config = None ):
327345 """Sets encrypts plaintext."""
@@ -335,7 +353,8 @@ def __str__(self):
335353 """Stringify the encrypted password for database storage."""
336354 if self .password is None :
337355 raise ValueError ('Password not set' )
338- return '{%s}%s' % (self .scheme , self .password )
356+ return '{%s}%s' % (self .scheme , self .password )
357+
339358
340359def test ():
341360 # SHA
@@ -383,6 +402,7 @@ def test():
383402 assert 'sekrit' == p
384403 assert 'not sekrit' != p
385404
405+
386406if __name__ == '__main__' :
387407 test ()
388408
0 commit comments