Skip to content

Wall ink image formats and image handling

jdeananderson edited this page Dec 7, 2018 · 32 revisions

Image file types used in this project

PBM image file

A pbm image file is a standard portable bitmap file. Pbm files can be created in Gimp, Photoshop, or in many other standard image editors. A pbm has a few lines of text at the beginning of the file describing the size of the image. The rest of the file is raw "1"s and "0"s representing whether each pixel of the image is black or white.

Raw image file

A raw image file, as far as the Wall-ink project is concerned, is similar to a pbm, but with the header stripped off. It simply is just the "1"s and "0"s of each pixel of the image and whether or not they are black or white. "Raw" files are useful for the wall-ink devices since the device e-ink screens need only to know if each pixel on the screen is on or off. The raw images used to be compressed, but the compression algorithm was removed because it was determined that it wasn't worth the complexity to take a file from 30 kb to 7 kb.

Wink image file

A wink image file is a raw image file, unique to this project, with a special header tacked on the front that is used specifically by a wall-ink device for displaying the raw image on it's e-ink screen. The Wink header contains information such as how long the image is to be displayed before the wall-ink device checks back in with the server.

Png image files

Png images of Wall-ink displays are created for convenience to be displayed in the wall-ink-server device manager. This is just a web-friendly copy of what is sent to each device.

Server processing of images

Various plugins will create content for wall-ink devices in various ways. They may use C++ code to write raw images, or they may use standard Linux command line tools to generate pbm files. Other plugins may convert standard jpg, png or bmp files into pbm files using ImageMagick to be further processed and sent to wall-ink devices. In the end, all images sent to a wall-ink device must be in the "wink" format as described in this article.

The Wall-ink-server takes a pbm file and strips off the pbm header using the project's Linux shell script pbmToRaw.sh. At this point, the image is considered to be in "raw" format. The server then takes this raw stream of "1"s and "0"s and places a new wall-ink-server specific header on the file containing sleep time and other such information to be delivered to the wall-ink device using rawToWink. RawToWink is a Linux command line tool included with the wall-ink-server project. Wall-ink devices will only accept files in "wink" format. The wink format is unique to this project as discussed below.

Wink file format

The "wink" or wall-ink file format is unique to this project.

Terms used to understand Wink formatting:

sha1 hash: A sha1 hash is a cryptographic hash function. See wikipedia article on sha1 for more information.

Unix time: The current 4-byte Unix time on the web server.

Wake time: The 4-byte Unix time when the wall-ink device should wake up from deep sleep.

Raw file: See discussion above on image file types

Image key: The passphrase used on both the server and the device to verify communications between server and client. See article on image security for more details.

mac_address: The MAC address of the device receiving the file. It is formatted as a C string with no colons or lowercase letters. For example: "2C3AE84BF392". Note that the null termination character at the end of the string is not hashed.

Wink file header

The Wink header consists of five distinct chunks. These five chunks are concatenated together into a 68-byte header. All values in the header are little endian. This 68-byte header is concatenated with the raw image file to make a complete ".wink" formatted file. The size of the raw image file will depend on the resolution of the image. The wink header format changed with firmware v4.00a and the matching server commit on Dec 3, 2018 to be more secure.

Header chunk 1: Time data message authentication code

The purpose of chunk 1 is to ensure that there is no data corruption in the transfer of the time data contained in chunks 2 and 3. If a single bit were to be flipped in chunk 3, the wall-ink device could be instructed to sleep for years instead of seconds or minutes. The message authentication code contained in chunk 1 also verifies that the first 28 bytes of the wink file were created by a machine with an identical image key, and for a device with that MAC address. Chunk 1 is 20 bytes and is created by using the following formula:

sha1( sha1(chunk2 . chunk3) sha1(mac_address) . sha1(image_key) )

In the above statement, the function sha1() calculates the sha1 hash and a period "." is concatenation.

Header chunk 2: Current Unix time

Chunk 2 is just the current Unix time from the web server. This chunk is 4 bytes in size. This data is used by the wall-ink devices to make adjustments to it's internal time calculations. See the discussion on [clock skew] for more information.

Header chunk 3: Wake-up time

Chunk 3, wake-up time, or nextTime is the time the wall-ink device is to wake up. It is in Unix time format and is 4 bytes in size. The wall-ink device calculates how many microseconds it should deep sleep based on the current Unix time in Chunk 2, the wake-up time in Chunk 3, and the measured clock skew.

Header chunk 4: Image message authentication code

Chunk 4, or the "imageHash" is used to verify the integrity of the raw image, and that it is from a server that shares the same image key. It is also used to allow the device to immediately sleep if the new image is identical to the last one it received. Chunk 4 is 20 bytes in size and is calculated by using the following procedure:

sha1( sha1(mac_address) . sha1(raw_image) . sha1(image_key) )

Where again, sha1() is a procedure that performs a sha1 hash, and "." is concatenation.

Header chunk 5: Image & time message authentication code

Chunk 5 is used to verify that the image and time data were created at the same time, for the same device. This protects against certain types of message replay attacks. Chunk 5 is 20 bytes in size and is calculated by using the following procedure:

sha1( sha1(chunk 1 . chunk 2) . sha1(mac_address) . sha1(raw_image) . sha1(image_key) )

Why chunk 4 and chunk 5?

It may be believed that chunk 4 and chunk 5 are redundant and their purposes could be merged, but there is a purpose to them being separate. A wall-ink device stores chunk 4 in RTC memory when it updates it's e-ink screen. This allows the device to know what image is on the screen without having the memory to store and compare the entire image. Chunk 4 is unique to a particular image, whereas chunk 5 is unique to an image AND when it was sent. Chunk 4 is therefore used by the wall-ink device firmware to see if a newly received image is identical in content to what is currently being displayed on the device screen. If a newly received chunk 4 is identical to the chunk 4 stored in RTC memory from the previous screen update, the wall-ink device does not update the screen. This option saves battery life when identical images are repeatedly sent to the same device. Chunk 5 is necessary to eliminate hackers from issuing a replay attack where a previously sent legitimate wink file is sent to a wall-ink device at a later date.

Important Files

makefile

Builds the code and deploys it to the test server with make; builds the code and deploys it to the live server with make deploy

web/config/settings.cfg

Contains configuration for the database connections; must run make after each edit

cplusplussource/layouts.cpp

Contains the code used to generate individual image layouts

cplusplussource/image.cpp

Contains many of the libraries used by layouts.cpp to generate the images from reservation data.

cplusplussource/processImage.cpp

Contains the code used to convert an array of bytes into a processed image for the use of a display

cplusplussource/fonts.h

Contains include statements for lots of Adafruit fonts so they don't need to be in image.cpp or image.h

cplusplussource/letters.h

Contains a font that was found on stackoverflow; might be good to get rid of this.

cplusplussource/sha1.c, cplusplussource/sha1.h

Contains a library for sha1 hashing.

cplusplussource/rawToWink.cpp

Source code to convert a raw, binary image file to a .wink file for the use of a display.

cplusplussource/qr_code_generator/

Contains a C++ library used by image.cpp to generate QR codes from strings

cplusplussource/Adafruit-GFX-Library/

Contains a library used main for fonts in image.cpp

get_image.php

Takes in a MAC address, firmware version, error code, width, height and voltage. It updates the devices database. It then queries the devices database for additional information about the device. Finally, it serves up the image provided by the plugin associated with the device.

web/plugins/

Each file in this directory corresponds with and defines a plugin.

web/plugin_dependencies/

This directory contains extra files needed to make the various plugins function.

web/genimg

Binary Linux executable which takes in a file containing information about a screen & its associated room and spits out a .wink image for use on the displays. It uses statically linked libraries, so it should run on most Linux systems.

web/rawToWink

Binary Linux executable which takes in a raw, binary image and outputs a .wink file for use on the displays. Usage is: ./rawToWink rawImage outputImage.wink imageWidth imageHeight nextRefreshTime mac_address

nextRefreshTime is the number of seconds before the screen should check in again.

mac_address is the mac_address of the destination wall-ink device formatted as follows: AABBCCDDEE11

Must supply an image with the precisely correct resolution for the target display!