In this post, you’ll learn more about Python, which offers various built-in algorithms for cryptographic tasks.
We’ll also demonstrate hmac: One of the built-in algorithms for cryptographic tasks, used for cryptographic message signing and verification using Python and run it in the Python Debugging Tools by Python4Delphi to get the results.
The hmac module implements keyed-hashing for message authentication, as described in RFC 2104. The HMAC algorithm can be used to verify the integrity of information passed between applications or stored in a potentially vulnerable location.
The basic idea is to generate a cryptographic hash of the actual data combined with a shared secret key. The resulting hash can then be used to check the transmitted or stored message to determine a level of trust, without transmitting the secret key.
Table of Contents
How to implement Python cryptographic services using the built-in hmac library in Windows GUI App?
Prerequisites: Before we begin to work, download and install the latest Python for your platform. Follow the Python4Delphi installation instructions mentioned here. Alternatively, you can check out the easy instructions found in this video Getting started with Python4Delphi.
First, open and run our Python GUI using project Demo1 from Python4Delphi with RAD Studio. Then insert the script into the lower Memo, click the Execute button, and get the result in the upper Memo. You can find the Demo1 source on GitHub. The behind the scene details of how Delphi manages to run your Python code in this amazing Python GUI can be found at this link.
How can we use binary digests to produce printable digests?
Let’s try an example of Binary Digests to produce printable digests. Some web services (Google checkout, Amazon S3) use the base64 encoded version of the binary digest instead of the hexdigest. Run the following code in Python4Delphi GUI:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import base64 import hmac import hashlib with open('lorem.txt', 'rb') as f: body = f.read() hash = hmac.new( b'secret-shared-key-goes-here', body, hashlib.sha1, ) digest = hash.digest() print(base64.encodebytes(digest)) |
The base64 encoded string ends in a new line, which frequently needs to be stripped off when embedding the string in http headers or other formatting-sensitive contexts. Let’s see the output in Python GUI:
How to implement applications of message signatures using Python?
Next, let’s try a more advanced example: Applications of Message Signatures. HMAC authentication should be used for any public network service, and any time data is stored where security is important. For example, when sending data through a pipe or socket, that data should be signed and then the signature should be tested before the data is used.
Here are the steps to implement the Applications of Message Signatures:
- Establish a function to calculate a digest for a string, and a simple class to be instantiated and passed through a communication channel.
- Create a BytesIO buffer to represent the socket or pipe. This example uses a naive, but easy to parse, format for the data stream. The digest and length of the data are written, followed by a new line. The serialized representation of the object, generated by pickle, follows.
- For this example program, write two objects to the stream. the first is written using the correct digest value. The second object is written to the stream with an invalid digest, produced by calculating the digest for some other data instead of the pickle.
- Now that the data is in the BytesIO buffer, it can be read back out again. Start by reading the line of data with the digest and data length. Then read the remaining data, using the length value. pickle.load() could read directly from the stream, but that assumes a trusted data stream, and this data is not yet trusted enough to unpickle it. Reading the pickle as a string from the stream, without actually unpickling the object, is safer.
- Once the pickled data is in memory, the digest value can be recalculated and compared against the data read using compare_digest(). If the digests match, it is safe to trust the data and unpickle it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
import hashlib import hmac import io import pickle import pprint def make_digest(message): "Return a digest for the message." hash = hmac.new( b'secret-shared-key-goes-here', message, hashlib.sha1, ) return hash.hexdigest().encode('utf-8') class SimpleObject: """Demonstrate checking digests before unpickling. """ def __init__(self, name): self.name = name def __str__(self): return self.name # Simulate a writable socket or pipe with a buffer out_s = io.BytesIO() # Write a valid object to the stream: # digestnlengthnpickle o = SimpleObject('digest matches') pickled_data = pickle.dumps(o) digest = make_digest(pickled_data) header = b'%s %dn' % (digest, len(pickled_data)) print('WRITING: {}'.format(header)) out_s.write(header) out_s.write(pickled_data) # Write an invalid object to the stream o = SimpleObject('digest does not match') pickled_data = pickle.dumps(o) digest = make_digest(b'not the pickled data at all') header = b'%s %dn' % (digest, len(pickled_data)) print('nWRITING: {}'.format(header)) out_s.write(header) out_s.write(pickled_data) out_s.flush() # Simulate a readable socket or pipe with a buffer in_s = io.BytesIO(out_s.getvalue()) # Read the data while True: first_line = in_s.readline() if not first_line: break incoming_digest, incoming_length = first_line.split(b' ') incoming_length = int(incoming_length.decode('utf-8')) print('nREAD:', incoming_digest, incoming_length) incoming_pickled_data = in_s.read(incoming_length) actual_digest = make_digest(incoming_pickled_data) print('ACTUAL:', actual_digest) if hmac.compare_digest(actual_digest, incoming_digest): obj = pickle.loads(incoming_pickled_data) print('OK:', obj) else: print('WARNING: Data corruption') |
- The output shows that the first object is verified and the second is deemed “corrupted”, as expected:
Comparing two digests with a simple string or bytes comparison can be used in a timing attack to expose part or all of the secret key by passing digests of different lengths. compare_digest() implements a fast but constant-time comparison function to protect against timing attacks.
Congratulations! You have learned how to implement Python cryptographic services using the built-in hmac library in Python GUI for Delphi Windows App.
Check out Python4Delphi which easily allows you to build Python GUIs for Windows using Delphi.