Encrypting & decrypting sensor data on disk using Zymkey


Why do this?

If you are just getting started with Zymkey you may want to start by encrypting some data to disk. This would be useful if you are collecting data to be read/analyzed at later time; and you do not want to store your data ‘in the clear’.

Encrypting data on disk prevents a bad actor from imaging your SD card and gaining access to sensitive data that is being stored in the field. If bad actor were to successfully image the card, data would remain encrypted and locked with your Zymkey’s private key. If the Zymkey was also removed, then the bad actor would still not be able to access the data because each specific Zymkey is bound to a specific host hardware. Moving a specific Zymkey to another host hardware would fail the binding process and Zymkey would not perform any crypto functions, including decryption of data blobs.

Encrypting Data Blobs

The process we describe here can also be used more generally to encrypt binary large objects, or ‘data blobs’

###What you will need
If you have not already setup and bound your zymkey please visit the Getting Started page located here.. You will need a Raspberry Pi and a Zymkey I2c or USB for this initial step. Be sure to install the zymkey pypi package! sudo pip install zymkey

We will use temperature data from a DS18B20 OneWire probe. We will encrypt the data to disk using the zymkey python package, and then decrypt in a different session. For the purpose of this tutorial I will not be going over the circuit setup and one-wire configuration. That is adequately covered here. If you have questions however, I’m happy to help! :smile:

The process is very simple:

  1. Collect Measurements
  2. Encrypt data with Zymkey
  3. Safely store data locally
  4. Decrypt at a later time with Zymkey.

Locking Data

Below is a sample script you can use to encrypt sensor data. Most of the code is for data acquisition, not encryption. For the most part, we are only interested in lines 78 - 82 toward the bottom (Look for zymkey.client).

import base64
import time
import zymkey
import datetime
import logging
import argparse
import subprocess as sub

class TempRead(object):
    def __init__(self):
        self.base_dir = '/sys/bus/w1/devices/'

    def probe_scan(self):
        devices_raw = sub.check_output(['ls', self.base_dir])
        devices = []
        for i in range(0, len(devices_raw)-3):
            if devices_raw[i:i+3] == '28-':

        return devices

    def read_temp_raw(self, probe):
        f = open(self.base_dir+probe+'/w1_slave', 'r')
        lines = f.readlines()
        return lines

    def read_temp(self, probe):
        lines = self.read_temp_raw(probe)

        while lines[0].strip()[-3:] != 'YES':
            lines = self.read_temp_raw(probe)
        equals_pos = lines[1].find('t=')

        if equals_pos != -1:
            temp_string = lines[1][equals_pos+2:]
            c = round(float(temp_string) / 1000, 1)
            f = round(float(c * 9 / 5 + 32), 1)
            return c, f

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--loglevel', '-l', default='debug')
    parser.add_argument('--file_out', '-fo', type=str, default='/tmp/encrypted.bin')
    parser.add_argument('--zymkey', action='store_true', default=False)
    parser.add_argument('--sleep', type=float, default=20)

    args = parser.parse_args()

        level=getattr(logging, args.loglevel.upper())
    )  # streams to sys.stderr by default

    temp = TempRead()
    probes = temp.probe_scan()

    encrypted_file = open(args.file_out, mode='wb')

    while True:
        for probe in probes:
            temp_c, temp_f = temp.read_temp(probe)
            payload = "action=data, " \
                "key=temperature, " \
                "tags.unit=c, " \
                "tags.sensor_id={}," \
                "timestamp={}, " \
                "value= {}".format(str("ds18b20:"+probe.replace("-", "")),
                                   datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),

            logging.debug('Unencrypted Payload: {}'.format(payload))

            if args.zymkey:
                logging.info('Encrypting data...')
                locked_data = zymkey.client.lock(bytearray(payload))
                logging.debug('Encrypted DATA: {}'.format(locked_data))


The takeaways here are the zymbit.client.lock method and the flow of data types. Specifically, we want to lock a measurement of type float, the zymkey app utils library expects a bytearray, and the data needs to be base64.encoded so the blob can be written to a file on disk. This may sound like a lot but it can all be contained in two simple lines of code:

locked_data = zymkey.client.lock(bytearray(payload))

See! Zymkey is super easy to use! :laughing:

To see this in action copy this to your pi, save it as sensor_lock.py for consistency, and run the command:

python sensor_lock.py --zymkey --sleep=5

You should see encrypted data flowing and the new file /tmp/encrypted.bin, where your data is saved on disk.

###Unlocking Data
With the data we locked to disk above, lets unlock the data now so it is human and machine readable. A simple script such as the one pasted below will read the encrypted file, decode with base64, unlock with zymkey and write to a decrypted.txt file so data is legible.

import base64
import logging
import zymkey
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--loglevel', '-l', default='debug')
parser.add_argument('--file_in', '-fi', type=str, default='/tmp/encrypted.bin')
parser.add_argument('--file_out', '-fo', type=str, default='/tmp/decrypted.txt')


args = parser.parse_args()

    level=getattr(logging, args.loglevel.upper())
)  # streams to sys.stderr by default

with open(args.file_in) as f:
    logging.debug("Reading encrypted data from {}".format(args.file_in))
    content = f.readlines()

decrypted_file = open(args.file_out, mode='w+')

for i in content:
    payload = zymkey.client.unlock(bytearray(base64.b64decode(i)))
    logging.debug("Writing decrypted sensor data {} to {}".format(payload, args.file_out))
    decrypted_file.write(str(payload) + '\n')


Copy this to your Raspberry Pi, save as sensor_unlock.py and run as is with:

python sensor_unlock.py

Your data will be unlocked and saved to disk at /tmp/decrypted.txt. It should look something like this:

action=data, key=temperature, tags.unit=c, tags.sensor_id=ds18b20:28000006151b77,timestamp=2016-12-13 22:55:02, value= 19.5
action=data, key=temperature, tags.unit=c, tags.sensor_id=ds18b20:280000061543fd,timestamp=2016-12-13 22:55:03, value= 19.6
action=data, key=temperature, tags.unit=c, tags.sensor_id=ds18b20:28000006156310,timestamp=2016-12-13 22:55:04, value= 19.3
action=data, key=temperature, tags.unit=c, tags.sensor_id=ds18b20:28000006e10735,timestamp=2016-12-13 22:55:05, value= 19.4

Getting Started with ZYMKEY 4i
Getting Started with ZYMKEY 3i
Getting Started with Zymkey USB
Getting Started with ZYMKEY 2i

Hello! I was trying to use the zymkey.client.lock() function on a Raspberry Pi 3 running Raspbian jessie with the Zymkey 4 i module. However, every time I try to tun tre function, it returns an error “Assertionerror: bad return code -1”. This happens regardless of me using a bytearray or a basestring filename as input. Any idea why this could be happening?


Can you please tell me what sudo systemctl zkifc shows?
Also, can you post the entire python exception trace?


Hi Scott,

The systemctl command returns " unknown operation ‘zkifc’ ". Seems weird although I can use the zymkey for functions like controlling the led.

if I run " zymkey.client.lock(bytearray(“hello”,‘utf8’)) " the exception trace reads:

Traceback (most recent call last):
File “”, line 1, in
File “/usr/local/lib/python2.7/dist-packages/zymkey/module.py”, line 196, in lock
raise AssertionError(‘bad return code {!r}’.format(ret))
AssertionError: bad return code -1

Another thing I observed yesterday was when running the zk_crypto_test file. If i generate a signature with one string and I try to verify that signature with another string, it should have given false but it gives a true value and the program exits when it reaches the line
" raise Exception(‘verification should have failed, but passed’) "


I forgot something in the systemctl command: sudo systemctl status zkifc. Please run this and post.

If you are able to control the LED, it seems to me that you the python module might be out of sync with the zymkey daemon (zkifc).

At this point, I would recommend purging the zymkey software and reinstalling it.


This is the output of systemctl:

zkifc.service - Zymkey Interface Connector
Loaded: loaded (/etc/systemd/system/zkifc.service; enabled)
Active: active (running) since Wed 2018-04-25 16:36:13 UTC; 37min ago
Process: 653 ExecStartPre=/bin/bash -c mkdir -p {BASE_DIR} && chown -R zymbit.zymbit {BASE_DIR} (code=exited, status=0/SUCCESS)
Main PID: 659 (su)
CGroup: /system.slice/zkifc.service
‣ 659 /bin/su zymbit -s /bin/bash -c zkifc -s /var/lib/zymbit/

Apr 25 16:36:13 raspberrypi systemd[1]: Starting Zymkey Interface Connector…
Apr 25 16:36:13 raspberrypi systemd[1]: Started Zymkey Interface Connector.
Apr 25 16:36:13 raspberrypi su[659]: Successful su for zymbit by root
Apr 25 16:36:13 raspberrypi su[659]: + ??? root:zymbit
Apr 25 16:36:13 raspberrypi su[659]: pam_unix(su:session): session opened for user zymbit by (uid=0)

Sure, I will try reinstalling it and see if helps!


i tried reinstalling the software but to no avail. The same error persists.


When you reinstalled, did you perform a purge of the old zymkey software?
sudo apt-get purge zkifc libzk
sudo pip uninstall zku

Please run the above commands and then rerun the install script.


Hey scott! Thanks for the instructions! I think it works now!!