1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 """
18 Utility functions for keyczar package.
19
20 @author: arkajit.dey@gmail.com (Arkajit Dey)
21 """
22
23 import base64
24 import math
25 import sha
26
27 from Crypto.Util import randpool
28 from pyasn1.codec.der import decoder
29 from pyasn1.codec.der import encoder
30 from pyasn1.type import univ
31
32 import errors
33
34 HLEN = sha.digest_size
35
36
37
38
39
40
41
42
43
44
45
46
47
48 RSA_OID = univ.ObjectIdentifier('1.2.840.113549.1.1.1')
49 RSA_PARAMS = ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'invq']
50 DSA_OID = univ.ObjectIdentifier('1.2.840.10040.4.1')
51 DSA_PARAMS = ['p', 'q', 'g']
52 SHA1RSA_OID = univ.ObjectIdentifier('1.2.840.113549.1.1.5')
53 SHA1_OID = univ.ObjectIdentifier('1.3.14.3.2.26')
54
56 seq = univ.Sequence()
57 for i in range(len(vals)):
58 seq.setComponentByPosition(i, vals[i])
59 return seq
60
62 return [seq.getComponentByPosition(i) for i in range(len(seq))]
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
104
106 oid = ASN1Sequence(RSA_OID, univ.Null())
107 key = univ.Sequence().setComponentByPosition(0, univ.Integer(0))
108 for i in range(len(RSA_PARAMS)):
109 key.setComponentByPosition(i+1, univ.Integer(params[RSA_PARAMS[i]]))
110 octkey = encoder.encode(key)
111 seq = ASN1Sequence(univ.Integer(0), oid, univ.OctetString(octkey))
112 return Encode(encoder.encode(seq))
113
115 alg_params = univ.Sequence()
116 for i in range(len(DSA_PARAMS)):
117 alg_params.setComponentByPosition(i, univ.Integer(params[DSA_PARAMS[i]]))
118 oid = ASN1Sequence(DSA_OID, alg_params)
119 octkey = encoder.encode(univ.Integer(params['x']))
120 seq = ASN1Sequence(univ.Integer(0), oid, univ.OctetString(octkey))
121 return Encode(encoder.encode(seq))
122
123
124
125
126
147
155
157 alg_params = ASN1Sequence(univ.Integer(params['p']),
158 univ.Integer(params['q']),
159 univ.Integer(params['g']))
160 oid = ASN1Sequence(DSA_OID, alg_params)
161 binkey = BytesToBin(encoder.encode(univ.Integer(params['y'])))
162 pubkey = univ.BitString("'%s'B" % binkey)
163 seq = ASN1Sequence(oid, pubkey)
164 return Encode(encoder.encode(seq))
165
167 """
168 Given the raw parameters of a DSA signature, return a Base64 signature.
169
170 @param r: parameter r of DSA signature
171 @type r: long int
172
173 @param s: parameter s of DSA signature
174 @type s: long int
175
176 @return: raw byte string formatted as an ASN.1 sequence of r and s
177 @rtype: string
178 """
179 seq = ASN1Sequence(univ.Integer(r), univ.Integer(s))
180 return encoder.encode(seq)
181
183 """
184 Given a raw byte string, return tuple of DSA signature parameters.
185
186 @param sig: byte string of ASN.1 representation
187 @type sig: string
188
189 @return: parameters r, s as a tuple
190 @rtype: tuple
191
192 @raise KeyczarErrror: if the DSA signature format is invalid
193 """
194 seq = decoder.decode(sig)[0]
195 if len(seq) != 2:
196 raise errors.KeyczarError("Illegal DSA signature.")
197 r = long(seq.getComponentByPosition(0))
198 s = long(seq.getComponentByPosition(1))
199 return (r, s)
200
202 """Algorithm EMSA_PKCS1-v1_5 from PKCS 1 version 2"""
203 magic_sha1_header = [0x30, 0x21, 0x30, 0x9, 0x6, 0x5, 0x2b, 0xe, 0x3, 0x2,
204 0x1a, 0x5, 0x0, 0x4, 0x14]
205 encoded = "".join([chr(c) for c in magic_sha1_header]) + Hash(msg)
206 pad_string = chr(0xFF) * (modulus_size / 8 - len(encoded) - 3)
207 return chr(1) + pad_string + chr(0) + encoded
208
210 """Convert bit string to byte string."""
211 bits = _PadByte(bits)
212 octets = [bits[8*i:8*(i+1)] for i in range(len(bits)/8)]
213 bytes = [chr(int(x, 2)) for x in octets]
214 return "".join(bytes)
215
217 """Convert byte string to bit string."""
218 return "".join([_PadByte(IntToBin(ord(byte))) for byte in bytes])
219
221 """Pad a string of bits with zeros to make its length a multiple of 8."""
222 r = len(bits) % 8
223 return ((8-r) % 8)*'0' + bits
224
226 if n == 0 or n == 1:
227 return str(n)
228 elif n % 2 == 0:
229 return IntToBin(n/2) + "0"
230 else:
231 return IntToBin(n/2) + "1"
232
234 """Return a big-endian byte string representation of an arbitrary length n."""
235 chars = []
236 while (n > 0):
237 chars.append(chr(n % 256))
238 n = n >> 8
239 chars.reverse()
240 return "".join(chars)
241
243 """Return byte string of 4 big-endian ordered bytes representing n."""
244 bytes = [m % 256 for m in [n >> 24, n >> 16, n >> 8, n]]
245 return "".join([chr(b) for b in bytes])
246
248 l = len(bytes)
249 return sum([ord(bytes[i]) * 256**(l - 1 - i) for i in range(l)])
250
252 """Return a ^ b as a byte string where a and b are byte strings."""
253
254 m = max(len(a), len(b))
255 a = _PadBytes(a, m - len(a))
256 b = _PadBytes(b, m - len(b))
257 x = [ord(c) for c in a]
258 y = [ord(c) for c in b]
259 z = [chr(x[i] ^ y[i]) for i in range(m)]
260 return _TrimBytes("".join(z))
261
263 """Prepend a byte string with n zero bytes."""
264 return n * chr(0) + bytes
265
267 """Trim leading zero bytes."""
268 trimmed = bytes.lstrip(chr(0))
269 if trimmed == "":
270 return chr(0)
271 else:
272 return trimmed
273
275 """Return n random bytes."""
276 return randpool.RandomPool(512).get_bytes(n)
277
279 """Return a SHA-1 hash over a variable number of inputs."""
280 md = sha.new()
281 for i in inputs:
282 md.update(i)
283 return md.digest()
284
286 """
287 Return Base64 encoding of s. Suppress padding characters (=).
288
289 Uses URL-safe alphabet: - replaces +, _ replaces /. Will convert s of type
290 unicode to string type first.
291
292 @param s: string to encode as Base64
293 @type s: string
294
295 @return: Base64 representation of s.
296 @rtype: string
297 """
298 return base64.urlsafe_b64encode(str(s)).replace("=", "")
299
300
302 """
303 Return decoded version of given Base64 string. Ignore whitespace.
304
305 Uses URL-safe alphabet: - replaces +, _ replaces /. Will convert s of type
306 unicode to string type first.
307
308 @param s: Base64 string to decode
309 @type s: string
310
311 @return: original string that was encoded as Base64
312 @rtype: string
313
314 @raise Base64DecodingError: If length of string (ignoring whitespace) is one
315 more than a multiple of four.
316 """
317 s = str(s.replace(" ", ""))
318 d = len(s) % 4
319 if d == 1:
320 raise errors.Base64DecodingError()
321 elif d == 2:
322 s += "=="
323 elif d == 3:
324 s += "="
325 return base64.urlsafe_b64decode(s)
326
328 """
329 Writes data to file at given location.
330
331 @param data: contents to be written to file
332 @type data: string
333
334 @param loc: name of file to write to
335 @type loc: string
336
337 @raise KeyczarError: if unable to write to file because of IOError
338 """
339 try:
340 f = open(loc, "w")
341 f.write(data)
342 f.close()
343 except IOError:
344 raise errors.KeyczarError("Unable to write to file %s." % loc)
345
347 """
348 Read data from file at given location.
349
350 @param loc: name of file to read from
351 @type loc: string
352
353 @return: contents of the file
354 @rtype: string
355
356 @raise KeyczarError: if unable to read from file because of IOError
357 """
358 try:
359 return open(loc).read()
360 except IOError:
361 raise errors.KeyczarError("Unable to read file %s." % loc)
362
363 -def MGF(seed, mlen):
364 """
365 Mask Generation Function (MGF1) with SHA-1 as hash.
366
367 @param seed: used to generate mask, a byte string
368 @type seed: string
369
370 @param mlen: desired length of mask
371 @type mlen: integer
372
373 @return: mask, byte string of length mlen
374 @rtype: string
375
376 @raise KeyczarError: if mask length too long, > 2^32 * hash_length
377 """
378 if mlen > 2**32 * HLEN:
379 raise errors.KeyczarError("MGF1 mask length too long.")
380 return ("".join([Hash(seed, IntToBytes(i))
381 for i in range(int(math.ceil(mlen / float(HLEN))))]))[:mlen]
382