demo_dynamic.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. """
  2. demo_dynamic.py v2b
  3. This program demonstrates Python's use of the dynamic
  4. language support additions to LTC, namely access to LTC
  5. constants, struct and union sizes, and the binding of a
  6. math package to LTC. Also provided are simple code
  7. fragments to illustrate how one might write a Python
  8. wrapper for LTC and how an app might call the wrapper.
  9. This or a similar model should work for Ruby and other
  10. dynamic languages.
  11. This instance uses Python's ctypes and requires a single
  12. .dylib linking together LTC and a math library. Building
  13. a single .dylib is needed because LTC wants a fairly tight
  14. relationship between itself and the mathlib. (ctypes can
  15. load multiple .dylibs, but it does not support this level
  16. of tight coupling between otherwise independent libraries.)
  17. My .dylib was created on OSX/macOS with the following:
  18. sudo make -j5 -f makefile.shared \
  19. CFLAGS="-DUSE_TFM -DTFM_DESC -I/usr/local/include" \
  20. EXTRALIBS=/usr/local/lib/libtfm.a install
  21. For python 2.7.12 on Ubuntu Xenial the following worked for
  22. me (without MPI support):
  23. sudo make -f makefile.shared install PREFIX="/usr"
  24. Reminder: you don't need to bind in a math library unless
  25. you are going to use LTC functions that need a
  26. mathlib. For example, public key crypto requires
  27. a mathlib; hashing and symmetric encryption do not.
  28. This code was written for Python 2.7 with the ctypes standard
  29. library.
  30. Larry Bugbee
  31. March 2014 v1
  32. August 2017 v2b
  33. """
  34. from ctypes import *
  35. from ctypes.util import find_library
  36. # switches to enable/disable selected output
  37. SHOW_ALL_CONSTANTS = True
  38. SHOW_ALL_SIZES = True
  39. SHOW_SELECTED_CONSTANTS = True
  40. SHOW_SELECTED_SIZES = True
  41. SHOW_BUILD_OPTIONS_ALGS = True
  42. SHOW_SHA256_EXAMPLE = True
  43. SHOW_CHACHA_EXAMPLE = True
  44. print
  45. print(' demo_dynamic.py')
  46. #-------------------------------------------------------------------------------
  47. # load the .dylib
  48. libname = 'tomcrypt'
  49. libpath = find_library(libname)
  50. print
  51. print(' path to library %s: %s' % (libname, libpath))
  52. LTC = cdll.LoadLibrary(libpath)
  53. print(' loaded: %s' % LTC)
  54. print
  55. #-------------------------------------------------------------------------------
  56. # get list of all supported constants followed by a list of all
  57. # supported sizes. One alternative: these lists may be parsed
  58. # and used as needed.
  59. if SHOW_ALL_CONSTANTS:
  60. print '-'*60
  61. print ' all supported constants and their values:'
  62. # get size to allocate for constants output list
  63. str_len = c_int(0)
  64. ret = LTC.crypt_list_all_constants(None, byref(str_len))
  65. print ' need to allocate %d bytes to build list \n' % str_len.value
  66. # allocate that size and get (name, size) pairs, each pair
  67. # separated by a newline char.
  68. names_sizes = c_buffer(str_len.value)
  69. ret = LTC.crypt_list_all_constants(names_sizes, byref(str_len))
  70. print names_sizes.value
  71. print
  72. if SHOW_ALL_SIZES:
  73. print '-'*60
  74. print ' all supported sizes:'
  75. # get size to allocate for sizes output list
  76. str_len = c_int(0)
  77. ret = LTC.crypt_list_all_sizes(None, byref(str_len))
  78. print ' need to allocate %d bytes to build list \n' % str_len.value
  79. # allocate that size and get (name, size) pairs, each pair
  80. # separated by a newline char.
  81. names_sizes = c_buffer(str_len.value)
  82. ret = LTC.crypt_list_all_sizes(names_sizes, byref(str_len))
  83. print names_sizes.value
  84. print
  85. #-------------------------------------------------------------------------------
  86. # get individually named constants and sizes
  87. # print selected constants
  88. if SHOW_SELECTED_CONSTANTS:
  89. print '-'*60
  90. print '\n selected constants:'
  91. names = [
  92. 'ENDIAN_LITTLE',
  93. 'ENDIAN_64BITWORD',
  94. 'PK_PUBLIC',
  95. 'MAX_RSA_SIZE',
  96. 'CTR_COUNTER_BIG_ENDIAN',
  97. ]
  98. for name in names:
  99. const_value = c_int(0)
  100. rc = LTC.crypt_get_constant(name, byref(const_value))
  101. value = const_value.value
  102. print ' %-25s %d' % (name, value)
  103. print
  104. # print selected sizes
  105. if SHOW_SELECTED_SIZES:
  106. print '-'*60
  107. print '\n selected sizes:'
  108. names = [
  109. 'rijndael_key',
  110. 'rsa_key',
  111. 'symmetric_CTR',
  112. 'twofish_key',
  113. 'ecc_point',
  114. 'gcm_state',
  115. 'sha512_state',
  116. ]
  117. for name in names:
  118. size_value = c_int(0)
  119. rc = LTC.crypt_get_size(name, byref(size_value))
  120. value = size_value.value
  121. print ' %-25s %d' % (name, value)
  122. print
  123. #-------------------------------------------------------------------------------
  124. #-------------------------------------------------------------------------------
  125. # LibTomCrypt exposes one interesting string that can be accessed
  126. # via Python's ctypes module, "crypt_build_settings", which
  127. # provides a list of this build's compiler switches and supported
  128. # algorithms. If someday LTC exposes other interesting strings,
  129. # they can be found with:
  130. # nm /usr/local/lib/libtomcrypt.dylib | grep " D "
  131. def get_named_string(lib, name):
  132. return c_char_p.in_dll(lib, name).value
  133. if SHOW_BUILD_OPTIONS_ALGS:
  134. print '-'*60
  135. print 'This is a string compiled into LTC showing compile '
  136. print 'options and algorithms supported by this build \n'
  137. print get_named_string(LTC, 'crypt_build_settings')
  138. #-------------------------------------------------------------------------------
  139. #-------------------------------------------------------------------------------
  140. # here is an example of how Python code can be written to access
  141. # LTC's implementation of SHA256 and ChaCha,
  142. # - - - - - - - - - - - - -
  143. # definitions
  144. def _get_size(name):
  145. size = c_int(0)
  146. rc = LTC.crypt_get_size(name, byref(size))
  147. if rc != 0:
  148. raise Exception('LTC.crypt_get_size(%s) rc = %d' % (name, rc))
  149. return size.value
  150. def _get_constant(name):
  151. constant = c_int(0)
  152. rc = LTC.crypt_get_constant(name, byref(constant))
  153. if rc != 0:
  154. raise Exception('LTC.crypt_get_constant(%s) rc = %d' % (name, rc))
  155. return constant.value
  156. def _err2str(err):
  157. # define return type
  158. errstr = LTC.error_to_string
  159. errstr.restype = c_char_p
  160. # get and return err string
  161. return errstr(err)
  162. CRYPT_OK = _get_constant('CRYPT_OK')
  163. class SHA256(object):
  164. def __init__(self):
  165. self.state = c_buffer(_get_size('sha256_state'))
  166. LTC.sha256_init(byref(self.state))
  167. def update(self, data):
  168. LTC.sha256_process(byref(self.state), data, len(data))
  169. def digest(self):
  170. md = c_buffer(32)
  171. LTC.sha256_done(byref(self.state), byref(md))
  172. return md.raw
  173. class ChaCha(object):
  174. def __init__(self, key, rounds):
  175. self.state = c_buffer(_get_size('chacha_state'))
  176. self.counter = c_int(1)
  177. err = LTC.chacha_setup(byref(self.state), key, len(key), rounds)
  178. if err != CRYPT_OK:
  179. raise Exception('LTC.chacha_setup(), err = %d, "%s"' % (err, _err2str(err)))
  180. def set_iv32(self, iv):
  181. err = LTC.chacha_ivctr32(byref(self.state), iv, len(iv), byref(self.counter))
  182. if err != CRYPT_OK:
  183. raise Exception('LTC.chacha_ivctr32(), err = %d, "%s"' % (err, _err2str(err)))
  184. def crypt(self, datain):
  185. dataout = c_buffer(len(datain))
  186. err = LTC.chacha_crypt(byref(self.state), datain, len(datain), byref(dataout))
  187. if err != CRYPT_OK:
  188. raise Exception('LTC.chacha_crypt(), err = %d, "%s"' % (err, _err2str(err)))
  189. return dataout.raw
  190. # - - - - - - - - - - - - -
  191. # a SHA256 app fragment
  192. # from wrapper import * # uncomment in real life
  193. if SHOW_SHA256_EXAMPLE:
  194. print '-'*60
  195. data = 'hello world'
  196. sha256 = SHA256()
  197. sha256.update(data)
  198. md = sha256.digest()
  199. template = '\n the SHA256 digest for "%s" is %s \n'
  200. print template % (data, md.encode('hex'))
  201. # - - - - - - - - - - - - -
  202. # a ChaCha app fragment
  203. if SHOW_CHACHA_EXAMPLE:
  204. print '-'*60
  205. key = 'hownowbrowncow\x00\x00' # exactly 16 or 32 bytes
  206. rounds = 12 # common values: 8, 12, 20
  207. iv = '123456789012' # exactly 12 bytes
  208. plain = 'Kilroy was here, there, and everywhere!'
  209. cha = ChaCha(key, rounds)
  210. cha.set_iv32(iv)
  211. cipher = cha.crypt(plain)
  212. template = '\n ChaCha%d ciphertext for "%s" is "%s"'
  213. print template % (rounds, plain, cipher.encode('hex'))
  214. # reset to decrypt
  215. cha.set_iv32(iv)
  216. decrypted = cha.crypt(cipher)
  217. template = ' ChaCha%d decoded text for "%s" is "%s" \n'
  218. print template % (rounds, plain, decrypted)
  219. # Footnote: Keys should be erased fm memory as soon as possible after use,
  220. # and that includes Python. For a tip on how to do that in Python, see
  221. # http://buggywhip.blogspot.com/2010/12/erase-keys-and-credit-card-numbers-in.html
  222. #-------------------------------------------------------------------------------
  223. #-------------------------------------------------------------------------------
  224. #-------------------------------------------------------------------------------