# Binaries
Haskell code compiled will always produce the same binary file, since the
compiler is deterministic (*).
(*)
GHCis not fully deterministic yet, but if you keep to the following setup:stack+LTS-Data.Unique, you shouldn’t have any problems. For more info, please look into the ongoing work in order to makeGHCsupport fully deterministic builds.
If a secure hash is applied to these binaries, in this case SHA-256, we can
create what is called a reproducible build hash. For example:
28066d57da7328899c853a3f6c9ebc1bc7e0fa1a0ce0e9bf05de9c796911aa93
This hexadecimal number, could be denominated as a warranty seal since it
certifies that a specific code will always produce the same binary.
As there is a link between code and binaries, this will allow the relevant
authorities to testify that the application that is currently being
executed comes from the source code and, in addition, to easily perform
trustworthy audits to verify that the applications, really do what they were
designed to do.
Here is an explanatory video showing how difficult it’s to make changes to a
binary in order to try to recreate the initial 256 bit hash:

- Safety in numbers: 3Blue1Brown - How secure is 256 bit security?
# Distribution
When using Docker technology for the distribution of binaries, as it is a
technology that does not give the same importance to determinism when it
comes to recreating images or containers, it has been necessary to create
algorithms that are capable of producing reproducible build hashes for both of
images and containers, and therefore safeguard the guarantees offered by the use
of Haskell.
- Docker Image Reproducible Build Hash:
#!/usr/bin/env bash
################################################################################
##
## Uniprocess, (c) 2018 SPISE MISU ApS, opensource.org/licenses/LGPL-3.0
##
################################################################################
clear
img="./tmp/img.tar"
docker save uniprocess > $img
echo "Docker Image Reproducible Build Hash:"
# exclude all opaque files
pat="*/*.wh..wh..opq"
# find all the layer.tar files
for f in $(tar -tf $img --wildcards "*/layer.tar"); do
# for each of the layer files, SHA256 each the files
for cf in $(tar -xf $img --to-stdout $f | tar --exclude="$pat" -tf -); do
n=${cf##*/}
if [ "" != "$n" ]; then
tar -xf $img --to-stdout $f | tar -xf - --to-stdout $cf |
sha256sum | cut -d " " -f 1
fi
done
# Hash the content of each file, sort them and combine to a single hash
done | sort | sha256sum | cut -d " " -f 1
find ./tmp -name "img.tar" -delete
echo
- Docker Container Reproducible Build Hash:
#!/usr/bin/env bash
################################################################################
##
## Uniprocess, (c) 2018 SPISE MISU ApS, opensource.org/licenses/LGPL-3.0
##
################################################################################
clear
uid=$1
con="./tmp/con.tar"
docker export $uid > $con
echo "Docker Container Reproducible Build Hash:"
# exclude (running) docker container mount file
pat="etc/mtab"
# find all the layer.tar files
for cf in $(tar --exclude="$pat" -tf $con); do
n=${cf##*/}
if [ "" != "$n" ]; then
tar -xf $con --to-stdout $cf |
sha256sum | cut -d " " -f 1
fi
# Hash the content of each file, sort them and combine to a single hash
done | sort | sha256sum | cut -d " " -f 1
find ./tmp -name "con.tar" -delete
echo
The reason for the use of Docker, is that it allows to use base containers
of a much smaller size if we compare it to a standard operating system. The
base container used is fpco/haskell-scratch:integer-gmp of only 2 MB
in size, producing container images of about 7.5 - 15 MB.
- Dockerfile.builder:
################################################################################
##
## Uniprocess, (c) 2018 SPISE MISU ApS, opensource.org/licenses/LGPL-3.0
##
################################################################################
FROM haskell:8.2
# Install dependecies needed to compile Haskell libraries
RUN apt-get update && apt-get install --yes \
xz-utils \
make
RUN stack --resolver lts-11.11 install base \
# A time & space-efficient byte arrays (Word8)
bytestring \
# Date and time stamps
time \
# Linux's kernel random number generator
random \
# Berkeley sockets
network \
# Native implementation of TLS protocol for Server & Client
tls \
# (tls) Default values for TLS parameters
data-default-class \
# (tls) Specify hash for validation (SHA256)
x509 \
# (tls) Accessing and storing X.509 certificates
x509-store \
# (tls) Disable validation for non x509 v3 certificates
x509-validation \
# Validate input. Example: A name shouldn't be "42"
parser-combinators \
# serializer from and to JSON
array \
# serializer from and to JSON
containers \
# serializer from and to JSON
mtl \
# serializer from and to JSON
syb
## References
# - Futtetennismo:
# * Base: https://futtetennismo.me/posts/docker/
# * File: 2017-11-24-docker-haskell-executables.html
- Dockerfile:
################################################################################
##
## Uniprocess, (c) 2018 SPISE MISU ApS, opensource.org/licenses/LGPL-3.0
##
################################################################################
FROM haskell-base-builder as builder
WORKDIR "../"
# copy the contents of the current directory in the working directory
COPY . .
RUN stack --resolver lts-11.11 install && \
strip /root/.local/bin/uniprocess
FROM fpco/haskell-scratch:integer-gmp
COPY --from=builder /root/.local/bin/uniprocess /bin/
COPY ./der/dev/console /dev/console
COPY ./der/env/.dockerenv /.dockerenv
COPY ./der/etc/hostname /etc/hostname
COPY ./der/etc/hosts /etc/hosts
COPY ./der/etc/resolv.conf /etc/resolv.conf
COPY ./tls/cacert.pem /tls/cacert.pem
COPY ./tls/uniprocess.public.crt /tls/uniprocess.public.crt
COPY ./tls/uniprocess.secret.key /tls/uniprocess.secret.key
# We provide the possibility to specify the port and an argument
# Examle: docker run -d -p 8443:8443 uniprocess 8443
# https://stackoverflow.com/a/40312311
# Cos we need root in order to server 80 and 443 in Linux, we use Tomcats 8080
# (HTTP) and 8443 (HTTPS) approach
#
# https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers
ENTRYPOINT [ "/bin/uniprocess" ]
CMD [ "8443" ] # Default value but can overriden at runtime
## References
# - Futtetennismo:
# * Base: https://futtetennismo.me/posts/docker/
# * File: 2017-11-24-docker-haskell-executables.html
And since the base container only includes Linux components to run Haskell
applications, this will minimize the attack surface for hackers.