PageRenderTime 65ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/OldUniversityStuff/ZIKS/ezPyCrypto-0.1.1/easy/ezPyCrypto.py

https://bitbucket.org/Behemot/university-rep
Python | 1334 lines | 1197 code | 20 blank | 117 comment | 21 complexity | c5937c935c720122d22e015c5a7b382c MD5 | raw file
Possible License(s): GPL-2.0
  1. #@+leo
  2. #@+node:0::@file easy/ezPyCrypto.py
  3. #@+body
  4. #@@language python
  5. #@<< ezPyCrypto declarations >>
  6. #@+node:1::<< ezPyCrypto declarations >>
  7. #@+body
  8. """
  9. ezPyCrypto - very simple API for military-grade cryptography
  10. in Python.
  11. Designed to be approachable for even total crypto newbies,
  12. this may be the only crypto API for Python you ever need.
  13. Features:
  14. - Create, Import and Export public keys and public/private keypairs - easy
  15. - Encrypt and Decrypt arbitrary-sized pieces of data, such as
  16. strings or files
  17. - Open up 'streams', so this object can be used as an encrypting/decrypting
  18. filter - good for socket-based comms and crypto of large files
  19. - Sign and Verify documents without fuss
  20. - Create private keys with or without a passphrase
  21. - Export private keys with a different (or no) passphrase
  22. - Sensible defaults - no need to specify a zillion options (or any options
  23. at all) unless you want to
  24. - Algorithms include RSA, ElGamal, DSA, ARC2, Blowfish, CAST, DES3, IDEA and RC5
  25. (default RSA and Blowfish)
  26. - Choose your own public and session key sizes (or accept defaults)
  27. Contains an easily-used yet versatile cryptography class, called
  28. L{key}, that performs stream and block encryption.
  29. Packaged with a suite of very simple example programs, which demonstrate
  30. ezPyCrypto and speed learning.
  31. ezPyCrypto requires the PyCrypto library (which I have hand-picked from
  32. several different Python crypto APIs, since it's the only
  33. API that doesn't lead its programmers on a wild goose chase
  34. of 3rd party libs, or require dozens/hundreds of lines of
  35. code just to do basic stuff, or lack in documentation.
  36. PyCrypto is available from http://pycrypto.sf.net)
  37. PyCrypto is a very usable and well implemented lower-level
  38. crypto API for Python. C backends give it speed, while
  39. well designed OO interface makes it relatively fast to learn.
  40. Also, it compiles cleanly and smoothly on Linux and Windows
  41. with no dramas.
  42. But I've written this module because PyCrypto is relatively
  43. low-level, and does present a harder learning curve for newbies.
  44. ezPyCrypto is written by David McNab <david@freenet.org.nz>
  45. Released under the GNU General Public License.
  46. No warranty, yada yada
  47. Refer to the documentation for class 'key' for more info.
  48. """
  49. from pdb import set_trace as trace
  50. import pickle
  51. import types
  52. import base64
  53. import zlib
  54. import Crypto
  55. from Crypto.PublicKey import ElGamal, DSA, RSA
  56. from Crypto.Util.randpool import RandomPool
  57. from Crypto.Util.number import getPrime
  58. from Crypto.Cipher import ARC2, Blowfish, CAST, DES3, IDEA, RC5
  59. from Crypto.Hash import MD5
  60. #@-body
  61. #@-node:1::<< ezPyCrypto declarations >>
  62. #@+others
  63. #@+node:2::exceptions
  64. #@+body
  65. # Define some exceptions for the various problems that can happen
  66. class CryptoKeyError(Exception):
  67. "Attempt to import invalid key"
  68. #@-body
  69. #@-node:2::exceptions
  70. #@+node:3::class key
  71. #@+body
  72. class key:
  73. """
  74. This may well be the only crypto class for Python that you'll ever need.
  75. Think of this class, and the ezPyCrypto module, as 'cryptography for
  76. the rest of us'.
  77. Designed to strike the optimal balance between ease of use, features
  78. and performance.
  79. Basic High-level methods:
  80. - L{encString} - encrypt a string
  81. - L{decString} - decrypt a string
  82. - L{encStringToAscii} - encrypt a string to a printable, mailable format
  83. - L{decStringFromAscii} - decrypt an ascii-format encrypted string
  84. - L{signString} - produce ascii-format signature of a string
  85. - L{verifyString} - verify a string against a signature
  86. - L{importKey} - import public key (and possibly private key too)
  87. - L{exportKey} - export public key only, as printable mailable string
  88. - L{exportKeyPrivate} - same, but export private key as well
  89. - L{makeNewKeys} - generate a new, random private/public key pair
  90. Middle-level (stream-oriented) methods:
  91. - L{encStart} - start a stream encryption session
  92. - L{encNext} - encrypt another piece of data
  93. - L{encEnd} - finalise stream encryption session
  94. - L{decStart} - start a stream decryption session
  95. - L{decNext} - decrypt the next piece of available data
  96. - L{decEnd} - finalise stream decryption session
  97. Low-level methods:
  98. - refer to the source code
  99. Principle of operation:
  100. - Data is encrypted with choice of symmetric block-mode session cipher
  101. (or default Blowfish if user doesn't care)
  102. - CFB block chaining is used for added security - each next block's
  103. key is affected by the previous block
  104. - The session key and initial value (IV) are encrypted against an RSA
  105. or ElGamal public key (user's choice, default RSA)
  106. - Each block in the stream is prepended with a 'length' byte, indicating
  107. how many bytes in the decrypted block are significant - needed when
  108. total data len mod block size is non-zero
  109. - Format of encrypted data is:
  110. - public key len - 2 bytes, little-endian - size of public key in bytes
  111. - public key - public key of recipient
  112. - block cipher len - unencrypted length byte - size of block cipher in bytes
  113. - block cipher - encrypted against public key, index into array
  114. of session algorithms
  115. - block key len - unencrypted length byte - size of block key in bytes
  116. - block key - encrypted against public key
  117. - block IV len - unencrypted length of block cipher IV - IV length in bytes
  118. - block cipher IV - encrypted against public key, prefixed 1-byte length
  119. - block1 len - 1 byte - number of significant chars in block1 *
  120. - block1 data - always 8 bytes, encrypted against session key
  121. - ...
  122. - blockn len
  123. - blockn data
  124. - If last data block is of the same size as the session cipher blocksize,
  125. a final byte 0x00 is sent.
  126. """
  127. #@<< class key declarations >>
  128. #@+node:1::<< class key declarations >>
  129. #@+body
  130. # Various lookup tables for encryption algorithms
  131. _algosPub = {'ElGamal':ElGamal, 'RSA':RSA}
  132. _algosPub1 = {ElGamal:'ElGamal', RSA:'RSA'}
  133. _algosSes = { "ARC2":ARC2, "Blowfish":Blowfish, "CAST":CAST,
  134. "DES3":DES3, "IDEA":IDEA, "RC5":RC5}
  135. _algosSes1 = {'ARC2':0, 'Blowfish':1, 'CAST':2, 'DES3':3, 'IDEA':4, 'RC5':5}
  136. _algosSes2 = [ARC2, Blowfish, CAST, DES3, IDEA, RC5]
  137. _algosSes3 = {ARC2:'ARC2', Blowfish:'Blowfish', CAST:'CAST',
  138. DES3:'DES3', IDEA:'IDEA', RC5:'RC5'}
  139. # Generate IV for passphrase encryption
  140. _passIV = "w8Z4(51fKH#p{!29Q05HWcb@K 6(1qdyv{9|4=+gvji$chw!9$38^2cyGK#;}'@DHx%3)q_skvh4#0*="
  141. # Buffer for yet-to-be-encrypted stream data
  142. _encBuf = ''
  143. #@-body
  144. #@-node:1::<< class key declarations >>
  145. #@+others
  146. #@+node:2::__init__
  147. #@+body
  148. def __init__(self, something = 512, algoPub=None, algoSess=None, **kwds):
  149. """Constructor. Creates a key object
  150. This constructor, when creating the key object, does one of
  151. two things:
  152. 1. Creates a completely new keypair, OR
  153. 2. Imports an existing keypair
  154. Arguments:
  155. 1. If new keys are desired:
  156. - key size in bits (int), default 512 - advise at least 1536
  157. - algoPub - either 'RSA' or 'ElGamal' (default 'RSA')
  158. - algoSess - one of 'ARC2', 'Blowfish', 'CAST', 'DES3', 'IDEA', 'RC5',
  159. (default 'Blowfish')
  160. 2. If importing an existing key or keypair:
  161. - keyobj (string) - result of a prior exportKey() call
  162. Keywords:
  163. - passphrase - default '':
  164. - If creating new keypair, this passphrase is used to encrypt privkey when
  165. exporting.
  166. - If importing a new keypair, the passphrase is used to authenticate and
  167. grant/deny access to private key
  168. """
  169. passphrase = kwds.get('passphrase', '')
  170. if type(something) is types.IntType:
  171. # which public key algorithm did they choose?
  172. if algoPub == None:
  173. algoPub = 'RSA'
  174. algoP = self._algosPub.get(algoPub, None)
  175. if algoP == None:
  176. # Whoops - don't know that one
  177. raise Exception("AlgoPub must be one of 'ElGamel', 'RSA' or 'DSA'")
  178. self.algoPub = algoP
  179. self.algoPname = algoPub
  180. # which session key algorithm?
  181. if algoSess == None:
  182. algoSess = 'Blowfish'
  183. algoS = self._algosSes.get(algoSess, None)
  184. if algoS == None:
  185. # Whoops - don't know that session algorithm
  186. raise Exception("AlgoSess must be one of AES/ARC2/Blowfish/CAST/DES/DES3/IDEA/RC5")
  187. self.algoSes = algoS
  188. self.algoSname = algoSess
  189. # organise random data pool
  190. self.randpool = RandomPool()
  191. self.randfunc = self.randpool.get_bytes
  192. # now create the keypair
  193. self.makeNewKeys(something, passphrase=passphrase)
  194. elif type(something) is types.StringType:
  195. if algoPub != None:
  196. raise Exception("Don't specify algoPub if importing a key")
  197. if self.importKey(something, passphrase=passphrase) == False:
  198. raise CryptoKeyError(
  199. "Attempted to import invalid key, or passphrase is bad")
  200. self.randpool = RandomPool()
  201. self.randfunc = self.randpool.get_bytes
  202. else:
  203. raise Exception("Must pass keysize or importable keys")
  204. #@-body
  205. #@-node:2::__init__
  206. #@+node:3::makeNewKeys()
  207. #@+body
  208. def makeNewKeys(self, keysize=512, **kwds):
  209. """
  210. Creates a new keypair in cipher object, and a new session key
  211. Arguments:
  212. - keysize (default 512), advise at least 1536
  213. Returns:
  214. - None
  215. Keywords:
  216. - passphrase - used to secure exported private key - default '' (no passphrase)
  217. Keypair gets stored within the key object. Refer L{exportKey},
  218. L{exportKeyPrivate} and L{importKey}.
  219. Generally no need to call this yourself, since the constructor
  220. calls this in cases where you aren't instantiating with an
  221. importable key.
  222. """
  223. passphrase = kwds.get('passphrase', '')
  224. if passphrase == None:
  225. passphrase = ''
  226. self.passphrase = passphrase
  227. # set up a public key object
  228. self.randpool.stir()
  229. self.k = self.algoPub.generate(keysize, self.randfunc)
  230. self.randpool.stir()
  231. self._calcPubBlkSize()
  232. # Generate random session key
  233. self._genNewSessKey()
  234. # Create session cipher object
  235. self.randpool.stir()
  236. #trace()
  237. # Create a new block cipher object
  238. self._initBlkCipher()
  239. #@-body
  240. #@-node:3::makeNewKeys()
  241. #@+node:4::importKey()
  242. #@+body
  243. def importKey(self, keystring, **kwds):
  244. """
  245. Imports a public key or private/public key pair.
  246. (as previously exported from this object
  247. with the L{exportKey} or L{exportKeyPrivate} methods.)
  248. Arguments:
  249. - keystring - a string previously imported with
  250. L{exportKey} or L{exportKeyPrivate}
  251. Keywords:
  252. - passphrase - string (default '', meaning 'try to import without passphrase')
  253. Returns:
  254. - True if import successful, False if failed
  255. You don't have to call this if you instantiate your key object
  256. in 'import' mode - ie, by calling it with a previously exported key.
  257. Note - you shouldn't give a 'passphrase' when importing a public key.
  258. """
  259. passphrase = kwds.get('passphrase', '')
  260. if passphrase == None:
  261. passphrase = ''
  262. try:
  263. #k1 = keystring.split("<StartPycryptoKey>", 1)
  264. #k2 = k1[1].split("<EndPycryptoKey>")
  265. ##print "decoding:\n", k2[0]
  266. #k = base64.decodestring(k2[0])
  267. #trace()
  268. keypickle = self._unwrap("Key", keystring)
  269. keytuple = pickle.loads(keypickle)
  270. haspass, size, keyobj = keytuple
  271. if haspass:
  272. # decrypt against passphrase
  273. blksiz = 8 # lazy of me
  274. # create temporary symmetric cipher object for passphrase - hardwire to Blowfish
  275. ppCipher = Blowfish.new(passphrase,
  276. Blowfish.MODE_CFB,
  277. self._passIV[0:blksiz])
  278. enclen = len(keyobj)
  279. decpriv = ''
  280. i = 0
  281. while i < enclen:
  282. decbit = ppCipher.decrypt(keyobj[i:i+blksiz])
  283. decpriv += decbit
  284. i += blksiz
  285. keyobj = decpriv[0:size]
  286. self.algoPname, self.k = pickle.loads(keyobj)
  287. self.algoPub = self._algosPub[self.algoPname]
  288. #raise Exception("Tried to import Invalid Key")
  289. self._calcPubBlkSize()
  290. self.passphrase = passphrase
  291. return True
  292. except:
  293. return False
  294. #@-body
  295. #@-node:4::importKey()
  296. #@+node:5::exportKey()
  297. #@+body
  298. def exportKey(self):
  299. """
  300. Exports the public key as a printable string.
  301. Exported keys can be imported elsewhere into MyCipher instances
  302. with the L{importKey} method.
  303. Note that this object contains only the public key. If you want to
  304. export the private key as well, call L{exportKeyPrivate} instaead.
  305. Note also that the exported string is Base64-encoded, and safe for sending
  306. in email.
  307. Arguments:
  308. - None
  309. Returns:
  310. - a base64-encoded string containing an importable key
  311. """
  312. rawpub = self._rawPubKey()
  313. expTuple = (False, None, rawpub)
  314. expPickle = pickle.dumps(expTuple, True)
  315. return self._wrap("Key", expPickle)
  316. #@-body
  317. #@-node:5::exportKey()
  318. #@+node:6::exportKeyPrivate()
  319. #@+body
  320. def exportKeyPrivate(self, **kwds):
  321. """
  322. Exports public/private key pair as a printable string.
  323. This string is a binary string consisting of a pickled key object,
  324. that can be imported elsewhere into MyCipher instances
  325. with the L{importKey} method.
  326. Note that this object contains the public AND PRIVATE keys.
  327. Don't EVER email any keys you export with this function (unless you
  328. know what you're doing, and you encrypt the exported keys against
  329. another key). When in doubt, use L{exportKey} instead.
  330. Keep your private keys safe at all times. You have been warned.
  331. Note also that the exported string is Base64-encoded, and safe for sending
  332. in email.
  333. Arguments:
  334. - None
  335. Keywords:
  336. - passphrase - default (None) to using existing passphrase. Set to '' to export
  337. without passphrase (if this is really what you want to do!)
  338. Returns:
  339. - a base64-encoded string containing an importable key
  340. """
  341. passphrase = kwds.get('passphrase', None)
  342. if passphrase == None:
  343. passphrase = self.passphrase
  344. # exported key is a pickle of the tuple:
  345. # (haspassphrase, keylen, keypickle)
  346. # if using passphrase, 'keypickle' is encrypted against blowfish, and 'keylen'
  347. # indicates the number of significant bytes.
  348. rawpriv = pickle.dumps((self.algoPname, self.k), True)
  349. # prepare the key tuple, depending on whether we're using passphrases
  350. if passphrase != '':
  351. blksiz = 8 # i'm getting lazy, assuming 8 for blowfish
  352. # encrypt this against passphrase
  353. ppCipher = Blowfish.new(passphrase,
  354. Blowfish.MODE_CFB,
  355. self._passIV[0:blksiz])
  356. keylen = len(rawpriv)
  357. extras = (blksiz - (keylen % blksiz)) % blksiz
  358. rawpriv += self.randfunc(extras) # padd with random bytes
  359. newlen = len(rawpriv)
  360. encpriv = ''
  361. #print "newlen = %d" % newlen
  362. #trace()
  363. i = 0
  364. while i < newlen:
  365. rawbit = rawpriv[i:i+blksiz]
  366. encbit = ppCipher.encrypt(rawpriv[i:i+blksiz])
  367. #print "i=%d rawbit len=%d, encbit len=%d" % (i, len(rawbit), len(encbit))
  368. encpriv += encbit
  369. i += blksiz
  370. #print "keylen=%d, newlen=%d, len(encpriv)=%d" % (keylen, newlen, len(encpriv))
  371. #trace()
  372. keytuple = (True, keylen, encpriv)
  373. else:
  374. keytuple = (False, None, rawpriv)
  375. # prepare final pickle, base64 encode, wrap
  376. keypickle = pickle.dumps(keytuple, True)
  377. return self._wrap("Key", keypickle)
  378. #@-body
  379. #@-node:6::exportKeyPrivate()
  380. #@+node:7::encString()
  381. #@+body
  382. def encString(self, raw):
  383. """
  384. Encrypt a string of data
  385. High-level func. encrypts an entire string of data, returning the encrypted
  386. string as binary.
  387. Arguments:
  388. - raw string to encrypt
  389. Returns:
  390. - encrypted string as binary
  391. Note - the encrypted string can be stored in files, but I'd suggest
  392. not emailing them - use L{encStringToAscii} instead. The sole advantage
  393. of this method is that it produces more compact data, and works a bit faster.
  394. """
  395. # All the work gets done by the stream level
  396. self.encStart()
  397. # carve up into segments, because Python gets really slow
  398. # at manipulating large strings
  399. size = len(raw)
  400. bits = []
  401. pos = 0
  402. chunklen = 1024
  403. while pos < size:
  404. bits.append(self.encNext(raw[pos:pos+chunklen]))
  405. pos += chunklen
  406. bits.append(self.encEnd())
  407. return "".join(bits)
  408. #@-body
  409. #@-node:7::encString()
  410. #@+node:8::encStringToAscii()
  411. #@+body
  412. def encStringToAscii(self, raw):
  413. """
  414. Encrypts a string of data to printable ASCII format
  415. Use this method instead of L{encString}, unless size and speed are
  416. major issues.
  417. This method returns encrypted data in bracketed base64 format,
  418. safe for sending in email.
  419. Arguments:
  420. - raw - string to encrypt
  421. Returns:
  422. - enc - encrypted string, text-wrapped and Base-64 encoded, safe for
  423. mailing.
  424. There's an overhead with base64-encoding. It costs size, bandwidth and
  425. speed. Unless you need ascii-safety, use encString() instead.
  426. """
  427. enc = self.encString(raw)
  428. return self._wrap("Message", enc)
  429. #@-body
  430. #@-node:8::encStringToAscii()
  431. #@+node:9::decString()
  432. #@+body
  433. def decString(self, enc):
  434. """
  435. Decrypts a previously encrypted string.
  436. Arguments:
  437. - enc - string, previously encrypted in binary mode with encString
  438. Returns:
  439. - dec - raw decrypted string
  440. """
  441. chunklen = 1024
  442. size = len(enc)
  443. bits = []
  444. pos = 0
  445. self.decStart()
  446. # carve up into small chunks so we don't get any order n^2 on large strings
  447. while pos < size:
  448. bits.append(self.decNext(enc[pos:pos+chunklen]))
  449. pos += chunklen
  450. self.decEnd()
  451. dec = "".join(bits)
  452. return dec
  453. #@-body
  454. #@-node:9::decString()
  455. #@+node:10::decStringFromAscii()
  456. #@+body
  457. def decStringFromAscii(self, enc):
  458. """
  459. Decrypts a previously encrypted string in ASCII (base64)
  460. format, as created by encryptAscii()
  461. Arguments:
  462. - enc - ascii-encrypted string, as previously encrypted with
  463. encStringToAscii()
  464. Returns:
  465. - dec - decrypted string
  466. May generate an exception if the public key of the encrypted string
  467. doesn't match the public/private keypair in this key object.
  468. To work around this problem, either instantiate a key object with
  469. the saved keypair, or use the importKey() function.
  470. Exception will also occur if this object is not holding a private key
  471. (which can happen if you import a key which was previously exported
  472. via exportKey(). If you get this problem, use exportKeyPrivate() instead
  473. to export your keypair.
  474. """
  475. #trace()
  476. wrapped = self._unwrap("Message", enc)
  477. return self.decString(wrapped)
  478. #@-body
  479. #@-node:10::decStringFromAscii()
  480. #@+node:11::signString()
  481. #@+body
  482. def signString(self, raw):
  483. """
  484. Sign a string using private key
  485. Arguments:
  486. - raw - string to be signed
  487. Returns:
  488. - wrapped, base-64 encoded string of signature
  489. Note - private key must already be present in the key object.
  490. Call L{importKey} for the right private key first if needed.
  491. """
  492. # hash the key with MD5
  493. m = MD5.new()
  494. m.update(raw)
  495. d = m.digest()
  496. #print "sign: digest"
  497. #print repr(d)
  498. # sign the hash with our current public key cipher
  499. self.randpool.stir()
  500. k = getPrime(128, self.randfunc)
  501. self.randpool.stir()
  502. s = self.k.sign(d, k)
  503. # now wrap into a tuple with the public key cipher
  504. tup = (self.algoPname, s)
  505. # and pickle it
  506. p = pickle.dumps(tup, True)
  507. # lastly, wrap it into our base64
  508. w = self._wrap("Signature", p)
  509. return w
  510. #@-body
  511. #@-node:11::signString()
  512. #@+node:12::verifyString()
  513. #@+body
  514. def verifyString(self, raw, signature):
  515. """
  516. Verifies a string against a signature.
  517. Object must first have the correct public key loaded. (see
  518. L{importKey}). An exception will occur if this is not the case.
  519. Arguments:
  520. - raw - string to be verified
  521. - signature - as produced when key is signed with L{signString}
  522. Returns:
  523. - True if signature is authentic, or False if not
  524. """
  525. # unrwap the signature to a pickled tuple
  526. p = self._unwrap("Signature", signature)
  527. # unpickle
  528. algoname, rawsig = pickle.loads(p)
  529. # ensure we've got the right algorithm
  530. if algoname != self.algoPname:
  531. return False # wrong algorithm - automatic fail
  532. # hash the string
  533. m = MD5.new()
  534. m.update(raw)
  535. d = m.digest()
  536. #print "verify: digest"
  537. #print repr(d)
  538. # now verify the hash against sig
  539. if self.k.verify(d, rawsig):
  540. return True # signature valid, or very clever forgery
  541. else:
  542. return False # sorry
  543. #@-body
  544. #@-node:12::verifyString()
  545. #@+node:13::test()
  546. #@+body
  547. def test(self, raw):
  548. """
  549. Encrypts, then decrypts a string. What you get back should
  550. be the same as what you put in.
  551. This is totally useless - it just gives a way to test if this API
  552. is doing what it should.
  553. """
  554. enc = self.encString(raw)
  555. dec = self.decString(enc)
  556. return dec
  557. #@-body
  558. #@-node:13::test()
  559. #@+node:14::testAscii()
  560. #@+body
  561. def testAscii(self, raw):
  562. """
  563. Encrypts, then decrypts a string. What you get back should
  564. be the same as what you put in.
  565. This is totally useless - it just gives a way to test if this API
  566. is doing what it should.
  567. """
  568. enc = self.encStringToAscii(raw)
  569. dec = self.decStringFromAscii(enc)
  570. return dec
  571. #@-body
  572. #@-node:14::testAscii()
  573. #@+node:15::Stream Methods
  574. #@+body
  575. # ---------------------------------------------
  576. #
  577. # These methods provide stream-level encryption
  578. #
  579. # ---------------------------------------------
  580. #@-body
  581. #@+node:1::encStart()
  582. #@+body
  583. def encStart(self):
  584. """
  585. Starts a stream encryption session
  586. Sets up internal buffers for accepting ad-hoc data.
  587. No arguments needed, nothing returned.
  588. """
  589. # Create a header block of segments, each segment is
  590. # encrypted against recipient's public key, to enable
  591. # recipient to decrypt the rest of the stream.
  592. # format of header block is:
  593. # - recipient public key
  594. # - stream algorithm id
  595. # - stream session key
  596. # - stream cipher initial value
  597. # Take algorithm index and pad it to the max length
  598. # stick in pubkey
  599. pubkey = self._rawPubKey()
  600. pubkeyLen = len(pubkey)
  601. self._tstSessKey0 = ''
  602. self._tstSessKey1 = ''
  603. self._tstIV0 = ''
  604. self._tstIV1 = ''
  605. self._tstBlk0 = ''
  606. self._tstBlk1 = ''
  607. #print "pub key len=%d" % pubkeyLen
  608. len0 = pubkeyLen % 256
  609. len1 = pubkeyLen / 256
  610. # Create algorithms info blk. Structure is:
  611. # 1byte - index into session ciphers table
  612. # 2bytes - session key len, LSB first
  613. # 1byte - session IV len, LSB first
  614. while 1:
  615. self._encHdrs = chr(len0) + chr(len1) + pubkey
  616. # add algorithms index
  617. algInfo = chr(self._algosSes2.index(self.algoSes))
  618. # Create new session key
  619. self._genNewSessKey()
  620. # add session key length
  621. sessKeyLen = len(self.sessKey)
  622. sessKeyLenL = sessKeyLen % 256
  623. sessKeyLenH = sessKeyLen / 256
  624. algInfo += chr(sessKeyLenL) + chr(sessKeyLenH)
  625. # add session IV length
  626. sessIVLen = len(self.sessIV)
  627. algInfo += chr(sessIVLen)
  628. #alg += self.randfunc(self.pubBlkSize - 1) # add random chaff
  629. #encAlgNum = self._encRawPub(alg)
  630. encAlgEnc = self._encRawPub(self._padToPubBlkSize(algInfo))
  631. if encAlgEnc == None:
  632. continue
  633. #encAlgLen = len(encAlgNum)
  634. #self._encHdrs += chr(encAlgLen) + encAlgNum
  635. self._encHdrs += encAlgEnc
  636. # ensure we can encrypt session key in one hit
  637. if len(self.sessKey) > self.pubBlkSize:
  638. raise Exception(
  639. "encStart: you need a bigger public key length")
  640. # encrypt and add session key
  641. sKeyEnc = self._encRawPub(self._padToPubBlkSize(self.sessKey))
  642. if sKeyEnc == None:
  643. continue
  644. # sKeyLen = len(sKeyEnc)
  645. # self._encHdrs += chr(sKeyLen) + sKeyEnc
  646. self._encHdrs += sKeyEnc
  647. # encrypt and add session cipher initial value
  648. sCipherInit = self._encRawPub(self._padToPubBlkSize(self.sessIV))
  649. if sCipherInit == None:
  650. continue
  651. # sCipherIVLen = len(sCipherInit)
  652. # self._encHdrs += chr(sCipherIVLen) + sCipherInit
  653. self._encHdrs += sCipherInit
  654. self._tstSessKey0 = self.sessKey
  655. self._tstIV0 = self.sessIV
  656. # Create a new block cipher object
  657. self._initBlkCipher()
  658. # ready to go!
  659. self._encBuf = ''
  660. # success
  661. break
  662. #@-body
  663. #@-node:1::encStart()
  664. #@+node:2::encNext()
  665. #@+body
  666. def encNext(self, raw=''):
  667. """
  668. Encrypt the next piece of data in a stream.
  669. Arguments:
  670. - raw - raw piece of data to encrypt
  671. Returns - one of:
  672. - '' - not enough data to encrypt yet - stored for later
  673. - encdata - string of encrypted data
  674. """
  675. if raw == '':
  676. return ''
  677. # grab any headers
  678. enc = self._encHdrs
  679. self._encHdrs = ''
  680. # add given string to our yet-to-be-encrypted buffer
  681. self._encBuf += raw
  682. # Loop on data, breaking it up and encrypting it in blocks. Don't
  683. # touch the last (n mod b) bytes in buffer, where n is total size and
  684. # b is blocksize
  685. size = len(self._encBuf)
  686. next = 0
  687. while next <= size - self.sesBlkSize: # skip trailing bytes for now
  688. # extract next block
  689. blk = self._encBuf[next:next+self.sesBlkSize]
  690. if self._tstBlk0 == '':
  691. self._tstBlk0 = blk
  692. # encrypt block against session key
  693. encpart = self.blkCipher.encrypt(blk)
  694. # add length byte and crypted block to internal buffer
  695. enc += chr(self.sesBlkSize) + encpart
  696. next += self.sesBlkSize
  697. # ditch what we've consumed from buffer
  698. self._encBuf = self._encBuf[next:]
  699. # return whatever we've encrypted so far
  700. return enc
  701. #@-body
  702. #@-node:2::encNext()
  703. #@+node:3::encEnd()
  704. #@+body
  705. def encEnd(self):
  706. """
  707. Called to terminate a stream session.
  708. Encrypts any remaining data in buffer.
  709. Arguments:
  710. - None
  711. Returns - one of:
  712. - last block of data, as a string
  713. """
  714. buf = ''
  715. if self._encBuf == '':
  716. # no trailing data - pass back empty packet
  717. return chr(0)
  718. # break up remaining data into packets, and encrypt
  719. while len(self._encBuf) > 0:
  720. # extract session blocksize worth of data from buf
  721. blk = self._encBuf[0:self.sesBlkSize]
  722. self._encBuf = self._encBuf[self.sesBlkSize:]
  723. blklen = len(blk)
  724. # pad if needed
  725. if blklen < self.sesBlkSize:
  726. blk += self.randfunc(self.sesBlkSize - blklen)
  727. # encrypt against session key, and add
  728. buf += chr(blklen)
  729. buf += self.blkCipher.encrypt(blk)
  730. # clean up and get out
  731. return buf
  732. #@-body
  733. #@-node:3::encEnd()
  734. #@+node:4::decStart()
  735. #@+body
  736. def decStart(self):
  737. """
  738. Start a stream decryption session.
  739. Call this method first, then feed in pieces of stream data into decNext until
  740. there's no more data to decrypt
  741. Arguments:
  742. - None
  743. Returns:
  744. - None
  745. """
  746. # Start with fresh buffer and initial state
  747. self._decBuf = ''
  748. self._decState = 'p'
  749. self._decEmpty = False
  750. self._tstSessKey1 = ''
  751. self._tstIV1 = ''
  752. self._tstBlk1 = ''
  753. # states - 'p'->awaiting public key
  754. # 'c'->awaiting cipher index
  755. # 'k'->awaiting session key
  756. # 'i'->awaiting cipher initial data
  757. # 'd'->awaiting data block
  758. #@-body
  759. #@-node:4::decStart()
  760. #@+node:5::decNext()
  761. #@+body
  762. def decNext(self, chunk):
  763. """
  764. Decrypt the next piece of incoming stream data.
  765. Arguments:
  766. - chunk - some more of the encrypted stream
  767. Returns (depending on state)
  768. - '' - no more decrypted data available just yet
  769. - data - the next available piece of decrypted data
  770. - None - session is complete - no more data available
  771. """
  772. if self._decEmpty:
  773. return None
  774. # add chunk to our buffer
  775. self._decBuf += chunk
  776. # bail out if nothing to do
  777. chunklen = len(self._decBuf)
  778. if chunklen < 2:
  779. return ''
  780. # start with empty decryption buffer
  781. decData = ''
  782. # loop around processing as much data as we can
  783. #print "decNext: started"
  784. while 1:
  785. if self._decState == 'p':
  786. size = ord(self._decBuf[0]) + 256 * ord(self._decBuf[1])
  787. if chunklen < size + 2:
  788. # don't have full pubkey yet
  789. return ''
  790. else:
  791. pubkey = self._decBuf[2:size+2]
  792. if not self._testPubKey(pubkey):
  793. raise Exception("Can't decrypt - public key mismatch")
  794. self._decBuf = self._decBuf[size+2:]
  795. self._decState = 'c'
  796. continue
  797. if self._decState == 'd':
  798. #trace()
  799. # awaiting next data chunk
  800. sizeReqd = self.sesBlkSize + 1
  801. size = len(self._decBuf)
  802. if size < sizeReqd:
  803. return decData
  804. nbytes = ord(self._decBuf[0])
  805. if nbytes == 0:
  806. self._decEmpty = True
  807. return None
  808. blk = self._decBuf[1:sizeReqd]
  809. self._decBuf = self._decBuf[sizeReqd:]
  810. decBlk = self.blkCipher.decrypt(blk)
  811. if self._tstBlk1 == '':
  812. self._tstBlk1 = decBlk
  813. decBlk = decBlk[0:nbytes]
  814. decData += decBlk
  815. if nbytes < self.sesBlkSize:
  816. self._decEmpty = True
  817. return decData
  818. continue
  819. if len(self._decBuf) < 2:
  820. return decData
  821. sizeReqd = ord(self._decBuf[0]) + 256 * ord(self._decBuf[1]) + 2
  822. size = len(self._decBuf)
  823. # bail if we have insufficient data
  824. if size < sizeReqd:
  825. return decData
  826. # extract length byte plus block
  827. #blksize = sizeReqd - 1
  828. #blk = self._decBuf[1:sizeReqd]
  829. #self._decBuf = self._decBuf[sizeReqd:]
  830. blk = self._decBuf[0:sizeReqd]
  831. self._decBuf = self._decBuf[sizeReqd:]
  832. # state-dependent processing
  833. if self._decState == 'c':
  834. #print "decrypting cipher info"
  835. # awaiting cipher info
  836. blk = self._decRawPub(blk)
  837. # session cipher index
  838. c = ord(blk[0])
  839. self.algoSes = self._algosSes2[c]
  840. # session key len
  841. self._tmpSessKeyLen = ord(blk[1]) + 256 * ord(blk[2])
  842. # session IV len
  843. self._tmpSessIVLen = ord(blk[3])
  844. # ignore the rest - it's just chaff
  845. self._decState = 'k'
  846. continue
  847. elif self._decState == 'k':
  848. # awaiting session key
  849. #print "decrypting session key"
  850. blk = self._decRawPub(blk)
  851. self.sessKey = blk[0:self._tmpSessKeyLen]
  852. self._tstSessKey1 = self.sessKey
  853. self._decState = 'i'
  854. continue
  855. elif self._decState == 'i':
  856. # awaiting cipher start value
  857. #print "decrypting IV"
  858. blk = self._decRawPub(blk)
  859. self.sessIV = blk[0:self._tmpSessIVLen]
  860. self._tstIV1 = self.sessIV
  861. # Create cipher object, now we have what we need
  862. self.blkCipher = self.algoSes.new(self.sessKey,
  863. getattr(self.algoSes, "MODE_CFB"),
  864. self.sessIV)
  865. self._calcSesBlkSize()
  866. self._decState = 'd'
  867. continue
  868. else:
  869. raise Exception(
  870. "decNext: strange state '%s'" % self._decState[0])
  871. #@-body
  872. #@-node:5::decNext()
  873. #@+node:6::decEnd()
  874. #@+body
  875. def decEnd(self):
  876. """
  877. Ends a stream decryption session.
  878. """
  879. # nothing really to do here - decNext() has taken care of it
  880. # just reset internal state
  881. self._decBuf = ''
  882. self._decState = 'c'
  883. #@-body
  884. #@-node:6::decEnd()
  885. #@-node:15::Stream Methods
  886. #@+node:16::Low Level
  887. #@+node:1::_wrap()
  888. #@+body
  889. def _wrap(self, type, msg):
  890. """
  891. Encodes message as base64 and wraps with <StartPyCryptoname>/<EndPycryptoname>
  892. Args:
  893. - type - string to use in header/footer - eg 'Key', 'Message'
  894. - msg - binary string to wrap
  895. """
  896. return "<StartPycrypto%s>\n%s<EndPycrypto%s>\n" \
  897. % (type, base64.encodestring(msg), type)
  898. #@-body
  899. #@-node:1::_wrap()
  900. #@+node:2::_unwrap()
  901. #@+body
  902. def _unwrap(self, type, msg):
  903. """
  904. Unwraps a previously _wrap()'ed message.
  905. """
  906. try:
  907. #trace()
  908. k1 = msg.split("<StartPycrypto%s>" % type, 1)
  909. k2 = k1[1].split("<EndPycrypto%s>" % type)
  910. k = k2[0]
  911. #print "raw = "
  912. #print k
  913. bin = base64.decodestring(k)
  914. return bin
  915. except:
  916. raise Exception("Tried to import Invalid %s" % type)
  917. self._calcBlkSize()
  918. #@-body
  919. #@-node:2::_unwrap()
  920. #@+node:3::_calcPubBlkSize()
  921. #@+body
  922. def _calcPubBlkSize(self):
  923. """
  924. Determine size of public key
  925. """
  926. self.pubBlkSize = (self.k.size() - 7) / 8
  927. #@-body
  928. #@-node:3::_calcPubBlkSize()
  929. #@+node:4::_encRawPub()
  930. #@+body
  931. def _encRawPub(self, raw):
  932. """
  933. Encrypt a small raw string using the public key
  934. algorithm. Input must not exceed the allowable
  935. block size.
  936. Arguments:
  937. - raw - small raw bit of string to encrypt
  938. Returns:
  939. - binary representation of encrypted chunk, or None if verify failed
  940. """
  941. if len(raw) > self.pubBlkSize:
  942. raise Exception(
  943. "_encraw: max len %d, passed %d bytes" % (self.pubBlkSize, len(raw)))
  944. self.randpool.stir()
  945. k = getPrime(128, self.randfunc)
  946. s = self.k.encrypt(raw, k)
  947. #d = self.k.decrypt(s)
  948. #if d != raw:
  949. # #print "_encRawPub: decrypt verify fail"
  950. # return None
  951. #trace()
  952. # format this tuple into <len><nitems><item1len><item1bytes><item2len><item2bytes>...
  953. enc = chr(len(s))
  954. for item in s:
  955. itemLen = len(item)
  956. itemLenL = itemLen % 256
  957. itemLenH = itemLen / 256
  958. #enc += chr(len(item))
  959. enc += chr(itemLenL) + chr(itemLenH)
  960. enc += item
  961. encLen = len(enc)
  962. encLenL = encLen % 256
  963. encLenH = encLen / 256
  964. #enc = chr(len(enc)) + enc
  965. enc = chr(encLenL) + chr(encLenH) + enc
  966. #d = self._decRawPub(enc)
  967. #if d != raw:
  968. # print "panic:_encRawPub: decrypt verify fail!"
  969. return enc
  970. #@-body
  971. #@-node:4::_encRawPub()
  972. #@+node:5::_decRawPub()
  973. #@+body
  974. def _decRawPub(self, enc):
  975. """
  976. Decrypt a public-key encrypted block, and return the decrypted string
  977. Arguments:
  978. - enc - the encrypted string, in the format as created by _encRawPub()
  979. Returns:
  980. - decrypted block
  981. """
  982. #trace()
  983. blklen = ord(enc[0]) + 256 * ord(enc[1])
  984. nparts = ord(enc[2])
  985. enc = enc[3:]
  986. if blklen != len(enc)+1:
  987. raise Exception(
  988. "_decRawPub: bad block length %d, should be %d" % (len(enc), blklen))
  989. parts = []
  990. for i in range(nparts):
  991. partlen = ord(enc[0]) + 256 * ord(enc[1])
  992. part = enc[2:partlen+2]
  993. enc = enc[partlen+2:]
  994. parts.append(part)
  995. partsTuple = tuple(parts)
  996. dec = self.k.decrypt(partsTuple)
  997. return dec
  998. #@-body
  999. #@-node:5::_decRawPub()
  1000. #@+node:6::_initBlkCipher()
  1001. #@+body
  1002. def _initBlkCipher(self):
  1003. """
  1004. Create a new block cipher object, set up with a new session key
  1005. and IV
  1006. """
  1007. self.blkCipher = self.algoSes.new(self.sessKey,
  1008. getattr(self.algoSes, "MODE_CFB"),
  1009. self.sessIV)
  1010. self._calcSesBlkSize()
  1011. #@-body
  1012. #@-node:6::_initBlkCipher()
  1013. #@+node:7::_calcSesBlkSize()
  1014. #@+body
  1015. def _calcSesBlkSize(self):
  1016. """
  1017. Determine size of session blocks
  1018. """
  1019. self.sesBlkSize = (self.blkCipher.block_size)
  1020. #@-body
  1021. #@-node:7::_calcSesBlkSize()
  1022. #@+node:8::_testPubKey()
  1023. #@+body
  1024. def _testPubKey(self, k):
  1025. """
  1026. Checks if binary-encoded key matches this object's pubkey
  1027. """
  1028. if k == self._rawPubKey():
  1029. return True
  1030. else:
  1031. return False
  1032. #@-body
  1033. #@-node:8::_testPubKey()
  1034. #@+node:9::_rawPubKey()
  1035. #@+body
  1036. def _rawPubKey(self):
  1037. """
  1038. Returns a binary-encoded string of public key
  1039. """
  1040. return pickle.dumps((self.algoPname, self.k.publickey()), True)
  1041. #@-body
  1042. #@-node:9::_rawPubKey()
  1043. #@+node:10::_padToPubBlkSize()
  1044. #@+body
  1045. def _padToPubBlkSize(self, raw):
  1046. """
  1047. padToPubBlkSize - pad a string to max size encryptable by public key
  1048. Defence against factoring attacks that can uplift a session key when
  1049. that key is encrypted by itself against public key
  1050. Arguments:
  1051. - raw - string to pad with random bytes
  1052. returns:
  1053. - padded string. Note - it is the responsibility of the decryption
  1054. code to know how much of the string to extract once decrypted.
  1055. """
  1056. rawlen = len(raw)
  1057. extras = self.randfunc(self.pubBlkSize - rawlen)
  1058. #print "padToPubBlkSize: len=%d, added %d bytes of chaff :)" \
  1059. # % (rawlen, len(extras))
  1060. return raw + extras
  1061. #@-body
  1062. #@-node:10::_padToPubBlkSize()
  1063. #@+node:11::_genNewSessKey()
  1064. #@+body
  1065. def _genNewSessKey(self):
  1066. """
  1067. Generate a new random session key
  1068. """
  1069. self.randpool.stir()
  1070. self.sessKey = self.randfunc(32)
  1071. self.randpool.stir()
  1072. self.sessIV = self.randfunc(8)
  1073. #@-body
  1074. #@-node:11::_genNewSessKey()
  1075. #@-node:16::Low Level
  1076. #@-others
  1077. #@-body
  1078. #@-node:3::class key
  1079. #@-others
  1080. #@-body
  1081. #@-node:0::@file easy/ezPyCrypto.py
  1082. #@-leo