comparison libtomcrypt/demos/demo_dynamic.py @ 1471:6dba84798cd5

Update to libtomcrypt 1.18.1, merged with Dropbear changes
author Matt Johnston <matt@ucc.asn.au>
date Fri, 09 Feb 2018 21:44:05 +0800
parents
children
comparison
equal deleted inserted replaced
1470:8bba51a55704 1471:6dba84798cd5
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
69 import sys
70 from ctypes import *
71 from ctypes.util import find_library
72
73 # switches to enable/disable selected output
74 SHOW_ALL_CONSTANTS = True
75 SHOW_ALL_SIZES = True
76 SHOW_SELECTED_CONSTANTS = True
77 SHOW_SELECTED_SIZES = True
78 SHOW_BUILD_OPTIONS_ALGS = True
79 SHOW_SHA256_EXAMPLE = True
80 SHOW_CHACHA_EXAMPLE = True
81
82 print(' ')
83 print(' demo_dynamic.py')
84
85 def 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
93 libname = 'tomcrypt'
94 libpath = find_library(libname)
95 print(' ')
96 print(' path to library %s: %s' % (libname, libpath))
97
98 LTC = cdll.LoadLibrary(libpath)
99 print(' loaded: %s' % LTC)
100 print(' ')
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
108 if 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
125 if 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
145 if 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
163 if 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
193 def get_named_string(lib, name):
194 return c_char_p.in_dll(lib, name).value.decode("utf-8")
195
196 if 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
212 from binascii import hexlify, unhexlify
213
214 def _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
221 def _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
228 def _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
235 CRYPT_OK = _get_constant(b'CRYPT_OK')
236
237 class 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
248 class 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
269 if 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
283 if 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 #-------------------------------------------------------------------------------