1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 """Represents cryptographic keys in Keyczar.
18
19 Identifies a key by its hash and type. Includes several subclasses
20 of base class Key.
21
22 @author: arkajit.dey@gmail.com (Arkajit Dey)
23 """
24
25 import hmac
26 import math
27 import random
28 import sha
29
30 from Crypto.Cipher import AES
31 from Crypto.PublicKey import DSA
32 from Crypto.PublicKey import RSA
33 import simplejson
34
35 import errors
36 import keyczar
37 import keyinfo
38 import util
39
40
41
42
43
44
45 -def GenKey(type, size=None):
72
96
98
99 """Parent class for Keyczar Keys."""
100
102 self.type = type
103 self.__size = self.type.default_size
104
106 if self.type.IsValidSize(new_size):
107 self.__size = new_size
108
110 """Return the key as a string. Abstract method."""
111
113 """Indirect getter for the key string."""
114 return self._GetKeyString()
115
120
122 """Indirect getter for hash."""
123 return self._Hash()
124
125 hash = property(__Hash, doc="""The hash id of the key.""")
126 size = property(lambda self: self.__size, __SetSize,
127 doc="""The size of the key in bits.""")
128 key_string = property(__GetKeyString, doc="""The key as a Base64 string.""")
129 key_bytes = property(lambda self: util.Decode(self.key_string),
130 doc="""The key as bytes.""")
131
135
137 """Parent class for symmetric keys such as AES, HMAC-SHA1"""
138
142
144 """Return the key as a string."""
145 return self.__key_string
146
148 """Parent class for asymmetric keys."""
149
153
155 """Represents AES symmetric private keys."""
156
164
166 return simplejson.dumps({"mode": str(self.mode),
167 "size": self.size,
168 "aesKeyString": self.key_string,
169 "hmacKey": simplejson.loads(str(self.hmac_key))})
170
176
177 @staticmethod
192
193 @staticmethod
195 """
196 Reads an AES key from a JSON string representation of it.
197
198 @param key: a JSON representation of an AES key
199 @type key: string
200
201 @return: an AES key
202 @rtype: L{AesKey}
203 """
204 aes = simplejson.loads(key)
205 hmac = aes['hmacKey']
206 return AesKey(aes['aesKeyString'],
207 HmacKey(hmac['hmacKeyString'], hmac['size']),
208 aes['size'], keyinfo.GetMode(aes['mode']))
209
211 """
212 Returns the data padded using PKCS5.
213
214 For a block size B and data with N bytes in the last block, PKCS5
215 pads the data with B-N bytes of the value B-N.
216
217 @param data: data to be padded
218 @type data: string
219
220 @return: PKCS5 padded string
221 @rtype: string
222 """
223 pad = self.block_size - len(data) % self.block_size
224 return data + pad * chr(pad)
225
227 """
228 Returns the unpadded version of a data padded using PKCS5.
229
230 @param padded: string padded with PKCS5
231 @type padded: string
232
233 @return: original, unpadded string
234 @rtype: string
235 """
236 pad = ord(padded[-1])
237 return padded[:-pad]
238
240 """
241 Return ciphertext byte string containing Header|IV|Ciph|Sig.
242
243 @param data: plaintext to be encrypted.
244 @type data: string
245
246 @return: raw byte string ciphertext formatted to have Header|IV|Ciph|Sig.
247 @rtype: string
248 """
249 data = self.__Pad(data)
250 iv_bytes = util.RandBytes(self.block_size)
251 ciph_bytes = AES.new(self.key_bytes, AES.MODE_CBC, iv_bytes).encrypt(data)
252 msg_bytes = self.Header() + iv_bytes + ciph_bytes
253 sig_bytes = self.hmac_key.Sign(msg_bytes)
254 return msg_bytes + sig_bytes
255
257 """
258 Decrypts the given ciphertext.
259
260 @param input_bytes: raw byte string formatted as Header|IV|Ciph|Sig where
261 Sig is the signature over the entire payload (Header|IV|Ciph).
262 @type input_bytes: string
263
264 @return: plaintext message
265 @rtype: string
266
267 @raise ShortCiphertextError: if the ciphertext is too short to have IV & Sig
268 @raise InvalidSignatureError: if the signature doesn't correspond to payload
269 """
270 data_bytes = input_bytes[keyczar.HEADER_SIZE:]
271 if len(data_bytes) < self.block_size + util.HLEN:
272 raise errors.ShortCiphertextError(len(data_bytes))
273
274 iv_bytes = data_bytes[:self.block_size]
275 ciph_bytes = data_bytes[self.block_size:-util.HLEN]
276 sig_bytes = data_bytes[-util.HLEN:]
277 if not self.hmac_key.Verify(input_bytes[:-util.HLEN], sig_bytes):
278 raise errors.InvalidSignatureError()
279
280 plain = AES.new(self.key_bytes, AES.MODE_CBC, iv_bytes).decrypt(ciph_bytes)
281 return self.__UnPad(plain)
282
284 """Represents HMAC-SHA1 symmetric private keys."""
285
289
291 return simplejson.dumps({"size": self.size,
292 "hmacKeyString": self.key_string})
293
294 @staticmethod
308
309 @staticmethod
311 """
312 Reads an HMAC-SHA1 key from a JSON string representation of it.
313
314 @param key: a JSON representation of an HMAC-SHA1 key
315 @type key: string
316
317 @return: an HMAC-SHA1 key
318 @rtype: L{HmacKey}
319 """
320 mac = simplejson.loads(key)
321 return HmacKey(mac['hmacKeyString'], mac['size'])
322
323 - def Sign(self, msg):
324 """
325 Return raw byte string of signature on the message.
326
327 @param msg: message to be signed
328 @type msg: string
329
330 @return: raw byte string signature
331 @rtype: string
332 """
333 return hmac.new(self.key_bytes, msg, sha).digest()
334
335 - def Verify(self, msg, sig_bytes):
336 """
337 Return True if the signature corresponds to the message.
338
339 @param msg: message that has been signed
340 @type msg: string
341
342 @param sig_bytes: raw byte string of the signature
343 @type sig_bytes: string
344
345 @return: True if signature is valid for message. False otherwise.
346 @rtype: boolean
347 """
348 return self.Sign(msg) == sig_bytes
349
351 """Represents private keys in Keyczar for asymmetric key pairs."""
352
353 - def __init__(self, type, params, pkcs8, pub):
357
359 return simplejson.dumps({"publicKey": simplejson.loads(
360 str(self.public_key)),
361 "pkcs8": self.pkcs8,
362 "size": self.size})
363
366
368 return self.public_key.hash
369
371 """Represents public keys in Keyczar for asymmetric key pairs."""
372
373 - def __init__(self, type, params, x509):
376
378 return simplejson.dumps({"x509": self.x509, "size": self.size})
379
382
384 """Represents DSA private keys in an asymmetric DSA key pair."""
385
391
392 @staticmethod
394 """
395 Return a newly generated DSA private key.
396
397 @param size: length of key in bits to generate
398 @type size: integer
399
400 @return: a DSA private key
401 @rtype: L{DsaPrivateKey}
402 """
403 key = DSA.generate(size, util.RandBytes)
404 params = {'g': key.g, 'p': key.p, 'q': key.q, 'y': key.y, 'x': key.x}
405 pubkey = key.publickey()
406 pub_params = {'g': pubkey.g, 'p': pubkey.p, 'q': pubkey.q, 'y': pubkey.y}
407 pub = DsaPublicKey(pub_params, util.ExportDsaX509(pub_params), pubkey, size)
408 return DsaPrivateKey(params, util.ExportDsaPkcs8(params), pub, key, size)
409
410 @staticmethod
412 """
413 Reads a DSA private key from a JSON string representation of it.
414
415 @param key: a JSON representation of a DSA private key
416 @type key: string
417
418 @return: an DSA private key
419 @rtype: L{DsaPrivateKey}
420 """
421 dsa = simplejson.loads(key)
422 pub = DsaPublicKey.Read(simplejson.dumps(dsa['publicKey']))
423 params = util.ParsePkcs8(dsa['pkcs8'])
424 key = DSA.construct((pub._params['y'], params['g'], params['p'], params['q'],
425 params['x']))
426 return DsaPrivateKey(params, dsa['pkcs8'], pub, key, dsa['size'])
427
428 - def Sign(self, msg):
429 """
430 Return raw byte string of signature on the message.
431
432 @param msg: message to be signed
433 @type msg: string
434
435 @return: byte string formatted as an ASN.1 sequnce of r and s
436 @rtype: string
437 """
438 k = random.randint(2, self.key.q-1)
439 (r, s) = self.key.sign(util.Hash(msg), k)
440 return util.MakeDsaSig(r, s)
441
443 """@see: L{DsaPublicKey.Verify}"""
444 return self.public_key.Verify(msg, sig)
445
447 """Represents RSA private keys in an asymmetric RSA key pair."""
448
454
471
472 @staticmethod
474 """
475 Return a newly generated RSA private key.
476
477 @param size: length of key in bits to generate
478 @type size: integer
479
480 @return: a RSA private key
481 @rtype: L{RsaPrivateKey}
482 """
483 key = RSA.generate(size, util.RandBytes)
484 params = {'n': key.n, 'e': key.e, 'd': key.d, 'p': key.q, 'q': key.p,
485 'dp': key.d % (key.q - 1), 'dq': key.d % (key.p - 1),
486 'invq': key.u}
487
488
489
490 pubkey = key.publickey()
491 pub_params = {'n': pubkey.n, 'e': pubkey.e}
492 pub = RsaPublicKey(pub_params, util.ExportRsaX509(pub_params), pubkey, size)
493 return RsaPrivateKey(params, util.ExportRsaPkcs8(params), pub, key, size)
494
495 @staticmethod
497 """
498 Reads a RSA private key from a JSON string representation of it.
499
500 @param key: a JSON representation of a RSA private key
501 @type key: string
502
503 @return: a RSA private key
504 @rtype: L{RsaPrivateKey}
505 """
506 rsa = simplejson.loads(key)
507 pub = RsaPublicKey.Read(simplejson.dumps(rsa['publicKey']))
508 params = util.ParsePkcs8(rsa['pkcs8'])
509 key = RSA.construct((params['n'], params['e'], params['d'],
510 params['q'], params['p'], params['invq']))
511 return RsaPrivateKey(params, rsa['pkcs8'], pub, key, rsa['size'])
512
514 """@see: L{RsaPublicKey.Encrypt}"""
515 return self.public_key.Encrypt(data)
516
518 """
519 Decrypts the given ciphertext.
520
521 @param input_bytes: raw byte string formatted as Header|Ciphertext.
522 @type input_bytes: string
523
524 @return: plaintext message
525 @rtype: string
526 """
527 ciph_bytes = input_bytes[keyczar.HEADER_SIZE:]
528 decrypted = self.key.decrypt(ciph_bytes)
529 return self.__Decode(decrypted)
530
531 - def Sign(self, msg):
532 """
533 Return raw byte string of signature on the SHA-1 hash of the message.
534
535 @param msg: message to be signed
536 @type msg: string
537
538 @return: string representation of long int signature over message
539 @rtype: string
540 """
541 emsa_encoded = util.MakeEmsaMessage(msg, self.size)
542 return util.BigIntToBytes(self.key.sign(emsa_encoded, None)[0])
543
545 """@see: L{RsaPublicKey.Verify}"""
546 return self.public_key.Verify(msg, sig)
547
549
550 """Represents DSA public keys in an asymmetric DSA key pair."""
551
556
557 @staticmethod
559 """
560 Reads a DSA public key from a JSON string representation of it.
561
562 @param key: a JSON representation of a DSA public key
563 @type key: string
564
565 @return: a DSA public key
566 @rtype: L{DsaPublicKey}
567 """
568 dsa = simplejson.loads(key)
569 params = util.ParseX509(dsa['x509'])
570 pubkey = DSA.construct((params['y'], params['g'], params['p'], params['q']))
571 return DsaPublicKey(params, dsa['x509'], pubkey, dsa['size'])
572
574 """
575 Return True if the signature corresponds to the message.
576
577 @param msg: message that has been signed
578 @type msg: string
579
580 @param sig: raw byte string of the signature formatted as an ASN.1 sequence
581 of r and s
582 @type sig: string
583
584 @return: True if signature is valid for message. False otherwise.
585 @rtype: boolean
586 """
587 try:
588 (r, s) = util.ParseDsaSig(sig)
589 return self.key.verify(util.Hash(msg), (r, s))
590 except errors.KeyczarError:
591
592 return False
593
595 """Represents RSA public keys in an asymmetric RSA key pair."""
596
601
603 if len(p) >= 2**61:
604 raise errors.KeyczarError("OAEP parameter string too long.")
605 k = int(math.floor(math.log(self._params['n'], 256)) + 1)
606 if len(msg) > k - 2 * util.HLEN - 2:
607 raise errors.KeyczarError("Message too long to OAEP encode.")
608 ph = util.Hash(p)
609 ps = (k - len(msg) - 2 * util.HLEN - 2) * chr(0)
610 db = "".join([ph, ps, chr(1), msg])
611 seed = util.RandBytes(util.HLEN)
612 db_mask = util.MGF(seed, k - util.HLEN - 1)
613 masked_db = util.Xor(db, db_mask)
614 seed_mask = util.MGF(masked_db, util.HLEN)
615 masked_seed = util.Xor(seed, seed_mask)
616 return "".join([chr(0), masked_seed, masked_db])
617
618 @staticmethod
620 """
621 Reads a RSA public key from a JSON string representation of it.
622
623 @param key: a JSON representation of a RSA public key
624 @type key: string
625
626 @return: a RSA public key
627 @rtype: L{RsaPublicKey}
628 """
629 rsa = simplejson.loads(key)
630 params = util.ParseX509(rsa['x509'])
631 pubkey = RSA.construct((params['n'], params['e']))
632 return RsaPublicKey(params, rsa['x509'], pubkey, rsa['size'])
633
635 """
636 Return a raw byte string of the ciphertext in the form Header|Ciph.
637
638 @param data: message to be encrypted
639 @type data: string
640
641 @return: ciphertext formatted as Header|Ciph
642 @rtype: string
643 """
644 data = self.__Encode(data)
645 ciph_bytes = self.key.encrypt(data, None)[0]
646 return self.Header() + ciph_bytes
647
649 """
650 Return True if the signature corresponds to the message.
651
652 @param msg: message that has been signed
653 @type msg: string
654
655 @param sig: string representation of long int signature
656 @type sig: string
657
658 @return: True if signature is valid for the message hash. False otherwise.
659 @rtype: boolean
660 """
661 try:
662 return self.key.verify(util.MakeEmsaMessage(msg, self.size), (util.BytesToInt(sig),))
663 except ValueError:
664
665 return False
666