Mercurial > dropbear
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 #------------------------------------------------------------------------------- |