1 2 3""" 4 demo_dynamic.py v2b 5 6 This program demonstrates Python's use of the dynamic 7 language support additions to LTC, namely access to LTC 8 constants, struct and union sizes, and the binding of a 9 math package to LTC. Also provided are simple code 10 fragments to illustrate how one might write a Python 11 wrapper for LTC and how an app might call the wrapper. 12 This or a similar model should work for Ruby and other 13 dynamic languages. 14 15 This instance uses Python's ctypes and requires a single 16 .dylib linking together LTC and a math library. Building 17 a single .dylib is needed because LTC wants a fairly tight 18 relationship between itself and the mathlib. (ctypes can 19 load multiple .dylibs, but it does not support this level 20 of tight coupling between otherwise independent libraries.) 21 22 My .dylib was created on OSX/macOS with the following: 23 sudo make -j5 -f makefile.shared \ 24 CFLAGS="-DUSE_TFM -DTFM_DESC -I/usr/local/include" \ 25 EXTRALIBS=/usr/local/lib/libtfm.a install 26 27 For python 2.7.12 on Ubuntu Xenial the following worked for 28 me (without MPI support): 29 sudo make -f makefile.shared install PREFIX="/usr" 30 31 Reminder: you don't need to bind in a math library unless 32 you are going to use LTC functions that need a 33 mathlib. For example, public key crypto requires 34 a mathlib; hashing and symmetric encryption do not. 35 36 ------ 37 38 This code was originally written for Python 2.7 with the 39 ctypes standard library. This version is modified to run 40 under both Python 2.7 and 3.6. 41 42 Arguably the biggest change for Python3 has to do with 43 strings. Under Python2, native strings are ASCII bytes and 44 passing them to LTC is natural and requires no conversion. 45 Under Python3 all native strings are Unicode which requires 46 they be converted to bytes before use by LTC. 47 48 Note the following for Python3. 49 - ASCII keys, IVs and other string arguments must be 50 'bytes'. Define them with a 'b' prefix or convert 51 via the 'bytes()' function. 52 - "strings" returned from LTC are bytes and conversion 53 to Unicode might be necessary for proper printing. 54 If so, use <string>.decode('utf-8'). 55 - The Python2 'print' statement becomes a function in 56 Python3 which requires parenthesis, eg. 'print()'. 57 58 NB: Unicode is achieved under Python2 by either defining 59 a Unicode string with a 'u' prefix or passing ASCII 60 strings thru the 'unicode()' function. 61 62 Larry Bugbee 63 March 2014 v1 64 August 2017 v2b 65 66""" 67 68 69import sys 70from ctypes import * 71from ctypes.util import find_library 72 73# switches to enable/disable selected output 74SHOW_ALL_CONSTANTS = True 75SHOW_ALL_SIZES = True 76SHOW_SELECTED_CONSTANTS = True 77SHOW_SELECTED_SIZES = True 78SHOW_BUILD_OPTIONS_ALGS = True 79SHOW_SHA256_EXAMPLE = True 80SHOW_CHACHA_EXAMPLE = True 81 82print(' ') 83print(' demo_dynamic.py') 84 85def inprint(s, indent=0): 86 "prints strings indented, including multline strings" 87 for line in s.split('\n'): 88 print(' '*indent + line) 89 90#------------------------------------------------------------------------------- 91# load the .dylib 92 93libname = 'tomcrypt' 94libpath = find_library(libname) 95print(' ') 96print(' path to library %s: %s' % (libname, libpath)) 97 98LTC = cdll.LoadLibrary(libpath) 99print(' loaded: %s' % LTC) 100print(' ') 101 102 103#------------------------------------------------------------------------------- 104# get list of all supported constants followed by a list of all 105# supported sizes. One alternative: these lists may be parsed 106# and used as needed. 107 108if SHOW_ALL_CONSTANTS: 109 print('-'*60) 110 print(' all supported constants and their values:') 111 112 # get size to allocate for constants output list 113 str_len = c_int(0) 114 ret = LTC.crypt_list_all_constants(None, byref(str_len)) 115 print(' need to allocate %d bytes to build list \n' % str_len.value) 116 117 # allocate that size and get (name, size) pairs, each pair 118 # separated by a newline char. 119 names_sizes = c_buffer(str_len.value) 120 ret = LTC.crypt_list_all_constants(names_sizes, byref(str_len)) 121 print(names_sizes.value.decode("utf-8")) 122 print(' ') 123 124 125if SHOW_ALL_SIZES: 126 print('-'*60) 127 print(' all supported sizes:') 128 129 # get size to allocate for sizes output list 130 str_len = c_int(0) 131 ret = LTC.crypt_list_all_sizes(None, byref(str_len)) 132 print(' need to allocate %d bytes to build list \n' % str_len.value) 133 134 # allocate that size and get (name, size) pairs, each pair 135 # separated by a newline char. 136 names_sizes = c_buffer(str_len.value) 137 ret = LTC.crypt_list_all_sizes(names_sizes, byref(str_len)) 138 print(names_sizes.value.decode("utf-8")) 139 print(' ') 140 141 142#------------------------------------------------------------------------------- 143# get individually named constants and sizes 144 145if SHOW_SELECTED_CONSTANTS: 146 print('-'*60) 147 print('\n selected constants:') 148 149 names = [ 150 b'ENDIAN_LITTLE', 151 b'ENDIAN_64BITWORD', 152 b'PK_PUBLIC', 153 b'LTC_MILLER_RABIN_REPS', 154 b'CTR_COUNTER_BIG_ENDIAN', 155 ] 156 for name in names: 157 const_value = c_int(0) 158 rc = LTC.crypt_get_constant(name, byref(const_value)) 159 value = const_value.value 160 print(' %-25s %d' % (name.decode("utf-8"), value)) 161 print(' ') 162 163if SHOW_SELECTED_SIZES: 164 print('-'*60) 165 print('\n selected sizes:') 166 167 names = [ 168 b'rijndael_key', 169 b'rsa_key', 170 b'symmetric_CTR', 171 b'twofish_key', 172 b'ecc_point', 173 b'gcm_state', 174 b'sha512_state', 175 ] 176 for name in names: 177 size_value = c_int(0) 178 rc = LTC.crypt_get_size(name, byref(size_value)) 179 value = size_value.value 180 print(' %-25s %d' % (name.decode("utf-8"), value)) 181 print(' ') 182 183 184#------------------------------------------------------------------------------- 185#------------------------------------------------------------------------------- 186# LibTomCrypt exposes one interesting string that can be accessed 187# via Python's ctypes module, "crypt_build_settings", which 188# provides a list of this build's compiler switches and supported 189# algorithms. If someday LTC exposes other interesting strings, 190# they can be found with: 191# nm /usr/local/lib/libtomcrypt.dylib | grep " D " 192 193def get_named_string(lib, name): 194 return c_char_p.in_dll(lib, name).value.decode("utf-8") 195 196if SHOW_BUILD_OPTIONS_ALGS: 197 print('-'*60) 198 print('This is a string compiled into LTC showing compile') 199 print('options and algorithms supported by this build \n') 200# print(get_named_string(LTC, 'crypt_build_settings')) 201 inprint(get_named_string(LTC, 'crypt_build_settings'), 4) 202 203 204#------------------------------------------------------------------------------- 205#------------------------------------------------------------------------------- 206# here is an example of how Python code can be written to access 207# LTC's implementation of SHA256 and ChaCha, 208 209# - - - - - - - - - - - - - 210# definitions 211 212from binascii import hexlify, unhexlify 213 214def _err2str(err): 215 # define return type 216 errstr = LTC.error_to_string 217 errstr.restype = c_char_p 218 # get and return err string 219 return errstr(err) 220 221def _get_size(name): 222 size = c_int(0) 223 rc = LTC.crypt_get_size(bytes(name), byref(size)) 224 if rc != 0: 225 raise Exception('LTC.crypt_get_size(%s) rc = %d' % (name, rc)) 226 return size.value 227 228def _get_constant(name): 229 constant = c_int(0) 230 rc = LTC.crypt_get_constant(bytes(name), byref(constant)) 231 if rc != 0: 232 raise Exception('LTC.crypt_get_constant(%s) rc = %d' % (name, rc)) 233 return constant.value 234 235CRYPT_OK = _get_constant(b'CRYPT_OK') 236 237class SHA256(object): 238 def __init__(self): 239 self.state = c_buffer(_get_size(b'sha256_state')) 240 LTC.sha256_init(byref(self.state)) 241 def update(self, data): 242 LTC.sha256_process(byref(self.state), data, len(data)) 243 def digest(self): 244 md = c_buffer(32) 245 LTC.sha256_done(byref(self.state), byref(md)) 246 return md.raw 247 248class ChaCha(object): 249 def __init__(self, key, rounds): 250 self.state = c_buffer(_get_size(b'chacha_state')) 251 self.counter = c_int(1) 252 err = LTC.chacha_setup(byref(self.state), key, len(key), rounds) 253 if err != CRYPT_OK: 254 raise Exception('LTC.chacha_setup(), err = %d, "%s"' % (err, _err2str(err))) 255 def set_iv32(self, iv): 256 err = LTC.chacha_ivctr32(byref(self.state), iv, len(iv), byref(self.counter)) 257 if err != CRYPT_OK: 258 raise Exception('LTC.chacha_ivctr32(), err = %d, "%s"' % (err, _err2str(err))) 259 def crypt(self, datain): 260 dataout = c_buffer(len(datain)) 261 err = LTC.chacha_crypt(byref(self.state), datain, len(datain), byref(dataout)) 262 if err != CRYPT_OK: 263 raise Exception('LTC.chacha_crypt(), err = %d, "%s"' % (err, _err2str(err))) 264 return dataout.raw 265 266# - - - - - - - - - - - - - 267# a SHA256 app fragment 268 269if SHOW_SHA256_EXAMPLE: 270 print('-'*60) 271 data = b'hello world' # we want bytes, not Unicode 272 273 sha256 = SHA256() 274 sha256.update(data) 275 md = sha256.digest() 276 277 template = '\n the SHA256 digest for "%s" is %s \n' 278 print(template % (data, hexlify(md))) 279 280# - - - - - - - - - - - - - 281# a ChaCha app fragment 282 283if SHOW_CHACHA_EXAMPLE: 284 print('-'*60) 285 key = b'hownowbrowncow\x00\x00' # exactly 16 or 32 bytes 286 rounds = 12 # common values: 8, 12, 20 287 iv = b'123456789012' # exactly 12 bytes 288 plain = b'Kilroy was here, there, and everywhere!' 289 290 cha = ChaCha(key, rounds) 291 cha.set_iv32(iv) 292 cipher = cha.crypt(plain) 293 294 template = '\n ChaCha%d ciphertext for "%s" is "%s"' 295 print(template % (rounds, plain, hexlify(cipher))) 296 297 cha.set_iv32(iv) # reset to decrypt 298 decrypted = cha.crypt(cipher) 299 300 template = ' ChaCha%d decoded text for "%s" is "%s" \n' 301 print(template % (rounds, plain, decrypted.decode("utf-8"))) 302 303# Footnote: Keys should be erased fm memory as soon as possible after use, 304# and that includes Python. For a tip on how to do that in Python, see 305# http://buggywhip.blogspot.com/2010/12/erase-keys-and-credit-card-numbers-in.html 306 307#------------------------------------------------------------------------------- 308#------------------------------------------------------------------------------- 309#------------------------------------------------------------------------------- 310