irods  4.2.8
About: iRODS (the integrated Rule Oriented Data System) is a distributed data-management system for creating data grids, digital libraries, persistent archives, and real-time data systems.
  Fossies Dox: irods-4.2.8.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

password_obfuscation.py
Go to the documentation of this file.
1 import hashlib
2 import os
3 import time
4 
5 from . import six
6 from .exceptions import IrodsError, IrodsWarning
7 
8 seq_list = [
9  0xd768b678,
10  0xedfdaf56,
11  0x2420231b,
12  0x987098d8,
13  0xc1bdfeee,
14  0xf572341f,
15  0x478def3a,
16  0xa830d343,
17  0x774dfa2a,
18  0x6720731e,
19  0x346fa320,
20  0x6ffdf43a,
21  0x7723a320,
22  0xdf67d02e,
23  0x86ad240a,
24  0xe76d342e
25  ]
26 
27 #Don't forget to drink your Ovaltine
28 wheel = [
29  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
30  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
31  'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
32  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
33  'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
34  '!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/'
35  ]
36 
37 maximum_password_length = 42
38 
39 default_password_key = 'a9_3fker'
40 default_scramble_prefix = '.E_'
41 
42 #Decode a password from a .irodsA file
43 def decode(s, uid=None):
44  #This value lets us know which seq value to use
45  #Referred to as "rval" in the C code
46  seq_index = ord(s[6]) - ord('e')
47  seq = seq_list[seq_index]
48 
49  #How much we bitshift seq by when we use it
50  #Referred to as "addin_i" in the C code
51  #Since we're skipping five bytes that are normally read,
52  #we start at 15
53  bitshift = 15
54 
55  #The uid is used as a salt.
56  if uid is None:
57  uid = os.getuid()
58 
59  #The first byte is a dot, the next five are literally irrelevant
60  #garbage, and we already used the seventh one. The string to decode
61  #starts at byte eight.
62  encoded_string = s[7:]
63 
64  decoded_string = ''
65 
66  for c in encoded_string:
67  if ord(c) == 0:
68  break
69  #How far this character is from the target character in wheel
70  #Referred to as "add_in" in the C code
71  offset = ((seq >> bitshift) & 0x1f) + (uid & 0xf5f)
72 
73  bitshift += 3
74  if bitshift > 28:
75  bitshift = 0
76 
77  #The character is only encoded if it's one of the ones in wheel
78  if c in wheel:
79  #index of the target character in wheel
80  wheel_index = (len(wheel) + wheel.index(c) - offset) % len(wheel)
81  decoded_string += wheel[wheel_index]
82  else:
83  decoded_string += c
84 
85  return decoded_string
86 
87 #encode passwords to store in the .irodsA file
88 def encode(s, uid=None, mtime=None):
89  if len(s) > maximum_password_length:
90  raise IrodsException("Password exceeds maximum length of {maximum_password_length} characters.".format(**locals()))
91  #mtime & 65535 needs to be within 20 seconds of the
92  #.irodsA file's mtime & 65535
93  if mtime is None:
94  mtime = int(time.time())
95 
96  #How much we bitshift seq by when we use it
97  #Referred to as "addin_i" in the C code
98  #We can't skip the first five bytes this time,
99  #so we start at 0
100  bitshift = 0
101 
102  #The uid is used as a salt.
103  if uid is None:
104  uid = os.getuid()
105 
106  #This value lets us know which seq value to use
107  #Referred to as "rval" in the C code
108  #The C code is very specific about this being mtime & 15,
109  #but it's never checked. Let's use zero.
110  seq_index = 0
111  seq = seq_list[seq_index]
112 
113  to_encode = ''
114 
115  #The C code DOES really care about this value matching
116  #the seq_index, though
117  to_encode += chr(ord('S') - ((seq_index & 0x7) * 2))
118 
119  #And this is also a song and dance to
120  #convince the C code we are legitimate
121  to_encode += chr(((mtime >> 4) & 0xf) + ord('a'))
122  to_encode += chr((mtime & 0xf) + ord('a'))
123  to_encode += chr(((mtime >> 12) & 0xf) + ord('a'))
124  to_encode += chr(((mtime >> 8) & 0xf) + ord('a'))
125 
126  #We also want to actually encode the passed string
127  to_encode += s
128 
129  #Yeah, the string starts with a dot. Whatever.
130  encoded_string = '.'
131 
132  for c in to_encode:
133  if ord(c) == 0:
134  break
135 
136  #How far this character is from the target character in wheel
137  #Referred to as "add_in" in the C code
138  offset = ((seq >> bitshift) & 0x1f) + (uid & 0xf5f)
139 
140  bitshift += 3
141  if bitshift > 28:
142  bitshift = 0
143 
144  #The character is only encoded if it's one of the ones in wheel
145  if c in wheel:
146  #index of the target character in wheel
147  wheel_index = (wheel.index(c) + offset) % len(wheel)
148  encoded_string += wheel[wheel_index]
149  else:
150  encoded_string += c
151 
152  #insert the seq_index (which is NOT encoded):
153  encoded_string = chr(seq_index + ord('e')).join([
154  encoded_string[:6],
155  encoded_string[6:]
156  ])
157 
158  #aaaaand, append a null character. because we want to print
159  #a null character to the file. because that's a good idea.
160  encoded_string += chr(0)
161 
162  return encoded_string
163 
164 #Hash some stuff to create ANOTHER encoder ring.
165 def get_encoder_ring(key=default_password_key):
166 
167  md5_hasher = hashlib.md5()
168  #key (called keyBuf in the C) is padded
169  #or truncated to 100 characters
170  md5_hasher.update(key.ljust(100, chr(0))[:100].encode('ascii'))
171  first = md5_hasher.digest()
172 
173  md5_hasher = hashlib.md5()
174  md5_hasher.update(first)
175  second = md5_hasher.digest()
176 
177  md5_hasher = hashlib.md5()
178  md5_hasher.update(first + second)
179  third = md5_hasher.digest()
180 
181  return first + second + third + third
182 
183 #unscramble passwords stored in the database
184 def unscramble(s, key=default_password_key, scramble_prefix=default_scramble_prefix, block_chaining=False):
185  if key is None:
186  key=default_password_key
187 
188  if not s.startswith(scramble_prefix):
189  #not scrambled. or if it is, not
190  #in a way we can unscramble
191  return s
192 
193  #the prefix is nonsense, strip it off
194  to_unscramble = s[len(scramble_prefix):]
195 
196  #get our encoder ring (called cpKey in the C code)
197  encoder_ring = get_encoder_ring(key)
198  encoder_ring_index = 0
199 
200  #for block chaining
201  chain = 0
202 
203  unscrambled_string = ''
204  for c in to_unscramble:
205  if c in wheel:
206  #the index of the target character in wheel
207  wheel_index = (wheel.index(c) - six.indexbytes(encoder_ring, encoder_ring_index % 61) - chain) % len(wheel)
208  unscrambled_string += wheel[wheel_index]
209  if block_chaining:
210  chain = ord(c) & 0xff
211  else:
212  unscrambled_string += c
213  encoder_ring_index += 1
214 
215  return unscrambled_string
216 
217 #scramble passwords to store in the database
218 def scramble(s, key=default_password_key, scramble_prefix=default_scramble_prefix, block_chaining=False):
219  if len(s) > maximum_password_length:
220  raise IrodsException("Password exceeds maximum length of {maximum_password_length} characters.".format(**locals()))
221  if key is None:
222  key=default_password_key
223 
224  to_scramble = s
225 
226  #get our encoder ring (called cpKey in the C code)
227  encoder_ring = get_encoder_ring(key)
228  encoder_ring_index = 0
229 
230  #for block chaining
231  chain = 0
232 
233  scrambled_string = ''
234  for c in to_scramble:
235  if c in wheel:
236  #the index of the target character in wheel
237  wheel_index = (wheel.index(c) + six.indexbytes(encoder_ring, encoder_ring_index % 61) + chain) % len(wheel)
238  scrambled_string += wheel[wheel_index]
239  if block_chaining:
240  chain = ord(scrambled_string[-1]) & 0xff
241  else:
242  scrambled_string += c
243  encoder_ring_index += 1
244 
245  return scramble_prefix + scrambled_string
irods.password_obfuscation.scramble
def scramble(s, key=default_password_key, scramble_prefix=default_scramble_prefix, block_chaining=False)
Definition: password_obfuscation.py:218
irods.password_obfuscation.unscramble
def unscramble(s, key=default_password_key, scramble_prefix=default_scramble_prefix, block_chaining=False)
Definition: password_obfuscation.py:184
int
typedef int((*funcPtr)())
irods.password_obfuscation.decode
def decode(s, uid=None)
Definition: password_obfuscation.py:43
irods::join
std::string join(std::vector< std::string > &strs, const std::string &separator)
Definition: irods_serialization.cpp:169
irods.password_obfuscation.get_encoder_ring
def get_encoder_ring(key=default_password_key)
Definition: password_obfuscation.py:165
irods.password_obfuscation.encode
def encode(s, uid=None, mtime=None)
Definition: password_obfuscation.py:88