Signing GS1 Data Matrix Barcodes
As part of my exploration of inventory and how you might keep some kind of traceability in parts, I wanted to explore the idea of using signatures on GS1 formatted data matrix barcodes. To do this, I used a few Python libraries: cyptography, biip, Pillow, and pylibdmtx.
WARNING! I haven't really thought through the cryptography here, and you shouldn't trust me here. This is just an exploration of the ideas to see about their viability, and not a cryptographically secure solution.
Signing Keys
Before we can go any further, we need to create some keys for us to use. For now, this doesn't really matter that much. For size purposes, though, we are going to stick to elliptic curve crypto to keep the amount of data involved to a minimum.
Now that we have the public/private key pair, we need to work on generating the correct structure for a GS1 code. One of the keys of this system is to use a Function 1 Symbol Character (FNC1), which is used to both indicate the format of the sequence, and to (optionally) separate fields. The ASCII Group Separator (GS) character can also be used to separate variable length fields.
Now let's put together the information that we want to smash into the GS1 data:
Putting this together with the GS1 element string structure, we can use this format string:
Now, let's make sure this parses properly.
Now to turn this into a Datamatrix using the pylibdmtx library.
Adding a Signature
The next step is to take the information and combine it together with a signature. We are going to do this using the public/private key pair generated earlier. This will sign everything from gs1_format
except for the first byte (FNC1). We will also verify that it's properly round-tripping.
Now we can insert this into the Datamatrix. First, we need to convert it into safe ASCII characters. We will do this using base64 encoding. Unfortunately, we can't use ASCII85, as it contains invalid characters. Fortunately, it is only 88 characters (out of 90 available). Whew!
Finally, we can turn THAT back into a Datamatrix.
print(f"Length of the original GS1 data: {len(gs1_format)}")
print(f"Length of the signed GS1 data: {len(signed_gs1_format)}")
gs1_signed_message = GS1Message.parse(signed_gs1_format.decode("utf-8"))
print([es.ai.data_title for es in gs1_signed_message.element_strings])
print(f"HRI: {gs1_signed_message.as_hri()}")
display(convert_to_dm(signed_gs1_format))
Comments or Questions?
If you have any comments, questions, or topics you'd like to see covered, please feel free to either reach out to me on Mastodon (link below) or open an issue on Github.