Zymkey 4i, Ubuntu Desktop 21.10, Rpi4, LUKS

Hello everyone! Sharing out a success, and steps for using the zymkey 4i for backing LUKS FDE under Ubuntu 21.10 on an Rpi4.

Image your boot device with Server or Desktop, and apply LUKS encryption according to the procedure at: raspberrypi - LUKS Disk Encryption on Raspberry Pi 4 and Ubuntu Desktop 20.10 - Ask Ubuntu

Desktop, unlike Server, doesn’t have cryptsetup in it’s initramfs at first. Therefore, like the follow-on comment suggests, boot it up, apt install cryptsetup-initramfs and update-initramfs -u -k all before applying LUKS.

For 21.10, you can leave splash in the cmdline.txt entry because the splash screen understands the askpass prompt, and channels it through to a pretty screen.

Notably, Adiantum performs much better on the rpi 1-4 family only because they don’t expose aes hardware intrinsics. This is a fallback encryption algorithm for lower-end hardware.
→ Feature detect this rather than always using adiantum: grep aes /proc/cpuinfo

Physically install the zymkey 4i:

From Ubuntu 20.04 (focal fossa) Support - #10 by kontrasec, the software kit must be slightly modified. We’re using packages from focal on impish. Use this as the contents of install.sh, and then run it:

#!/bin/bash

#---------------------------------------------------------------------------------------------------------------------------------------------------------------
# Copyright (C) 2021 by copyright Zymbit
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without l> imitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#---------------------------------------------------------------------------------------------------------------------------------------------------------------

mod=""

# ensure running as root or exit
if [ "$(id -u)" != "0" ]; then
  echo "run this as root or use sudo" 2>&1 && exit 1;
fi;

# insure that the group 'gpio' exists
grep "^gpio" /etc/group &>/dev/null
if [ $? -ne 0 ]
then
  if [ "$1" == "-y" ]
  then
     answer="YES"
  else
    echo "Group 'gpio' does not exist. This group is necessary for zymbit software to operate normally."
    read -p 'Type yes in all capital letters (YES) to create this group: ' answer <&1
  fi
  if [ "${answer}" == "YES" ]
  then
    # Add group 'gpio'
    groupadd gpio
    # Modify /etc/rc.local to change the group of /etc/sys/class/gpio
    grep "chown -R root:gpio" /etc/rc.local &>/dev/null
    if [ $? -ne 0 ]
    then
      echo "chown -R root:gpio /sys/class/gpio" >> /etc/rc.local
      echo "chmod -R ug+rw /sys/class/gpio" >> /etc/rc.local
    fi
    # Check for existence of udev rule
    if [ ! -f "/etc/udev/rules.d/80-gpio-noroot.rules" ]
    then
      echo "ACTION==\"add\", SUBSYSTEM==\"gpio\", PROGRAM=\"/bin/sh -c 'chown -R root:gpio /sys/${DEVPATH}; chmod -R g+w /sys/${DEVPATH}'\"" >> /etc/udev/rules.d/80-gpio-noroot.rules
    fi
  else
    echo "Quitting..."
    exit -1
  fi
fi

function pip3()
{
   python3 -m pip $@
}

distro=`lsb_release -c | cut -f2 -d$'\t'`
uname -m | grep "arm"
if [ $? -eq 0 ]
then
   arch=""
else
   arch="-"`uname -m`
fi
distro="focal"

# for older versions of Raspbian, insure that apt-transport-https is installed first
echo -n "Installing prerequisites (this might take a few minutes)..."
apt update --allow-releaseinfo-change -y
apt install -y libboost-thread-dev lsb-release libjansson4 &>/dev/null
apt install -y apt-transport-https curl libyaml-dev libssl-dev libcurl4-openssl-dev python3-pip python3-setuptools python3-dev i2c-tools &>/dev/null
pip3 install inotify
pip3 install pycurl
pip3 install progress
pip3 install python-gnupg
echo "done."

baseurl="https://zk-sw-repo${mod}.s3.amazonaws.com"
# import zymbit gpg key
gpg_key_url="$baseurl/apt-zymkey-pubkey.gpg"
echo -n "Importing Zymbit Packages gpg key... "
# import the gpg key
curl -L "${gpg_key_url}" 2> /dev/null | apt-key add - &>/dev/null
echo "done."

# add zymbit apt repo to sources list
apt_source_path="/etc/apt/sources.list.d/zymbit.list"
echo -n "Installing $apt_source_path..."
echo "deb $baseurl/apt-repo-${distro}${arch}/ ${distro} main" > $apt_source_path
echo "done...Updating now."
apt update -y

# install our packages
echo -n "Installing Zymkey Packages..."
apt install -y libzk libzymkeyssl zkbootrtc zkifc zkapputilslib zksaapps zkpkcs11 cryptsetup &>/dev/nul || exit
pip3 install --upgrade zku &>/dev/null
pip3 install --upgrade zk_luks &>/dev/null

curl -G https://s3.amazonaws.com/zk-sw-repo/zk_prep_encr > /usr/local/bin/zk_prep_encr
chmod +x /usr/local/bin/zk_prep_encr

systemctl restart zkifc
sync
sleep 10

# reboot
echo "Rebooting now..."
reboot

Let the rpi4 reboot, then add a new key to the LUKS drive, and lock it with the zymkey 4i. As root/sudo:

# https://run.tournament.org.il/ubuntu-20-04-and-tpm2-encrypted-system-disk/
pushd /root

cat /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c 64 > root.key

# Retry this next line till it works.... :facepalm:
zklockifs root.key > /var/lib/zymbit/key.bin.lock

cryptsetup luksAddKey /dev/mmcblk0p2 root.key

rm root.key

popd

Now, add new hooks for initramfs generation. Here, Zymbit’s mk_encr_ext_rfs.sh script has a copy:

# Zymbit mk_encr_ext_rfs.sh

# Copy /var/lib/zymbit and all standalone zymkey utilities to initramfs
cat > /etc/initramfs-tools/hooks/zymkey_cryptfs_cfg <<"EOF"
#!/bin/sh

PREREQ=""

prereqs() {
     echo "$PREREQ"
}

case "$1" in
    prereqs)
        prereqs
        exit 0
        ;;
esac

. /usr/share/initramfs-tools/hook-functions

mkdir -p ${DESTDIR}/var/lib/zymbit
cp -prf /var/lib/zymbit/* ${DESTDIR}/var/lib/zymbit
copy_exec /sbin/zkunlockifs /sbin

EOF
chmod +x /etc/initramfs-tools/hooks/zymkey_cryptfs_cfg



# Bring the i2c drivers into initramfs
grep -q "^i2c-dev" ${crfsvol}/etc/initramfs-tools/modules || echo "i2c-dev" >> ${crfsvol}/etc/initramfs-tools/modules
grep -q "^i2c-bcm2835" ${crfsvol}/etc/initramfs-tools/modules || echo "i2c-bcm2835" >> ${crfsvol}/etc/initramfs-tools/modules
grep -q "^i2c-bcm2708" ${crfsvol}/etc/initramfs-tools/modules || echo "i2c-bcm2708" >> ${crfsvol}/etc/initramfs-tools/modules
grep -q "^lan78xx" ${crfsvol}/etc/initramfs-tools/modules || echo "lan78xx" >> ${crfsvol}/etc/initramfs-tools/modules

From Raspberry pi4 Ubuntu not booting - #10 by kontrasec, the stock keyscript has no fallbacks in case the zymbit is missing, damaged, or the OS was updated. Use this instead:

cat > /lib/cryptsetup/scripts/zk_get_key <<'EOF'
#!/bin/sh

num_times=30
while [ ${num_times} -gt 0 ]
do
    ls /sys/class/net/eth* 1>/dev/null 2>&1
    eth=$?
    ls /sys/class/net/enx* 1>/dev/null 2>&1
    enx=$?

    if [ ${eth} -ne 0 ] && [ ${enx} -ne 0 ]
    then
        num_times=$((num_times-1))
        sleep 0.1
    else
        break
fi
done
num_times=30
while [ ${num_times} -gt 0 ]
do
    if [ -d "/var/lib/zymbit" ]
    then
        break
    else
        num_times=$((num_times-1))
        sleep 0.1
    fi
done

if [ -e /var/lib/zymbit/zkenv.conf ]
then
    export $(cat /var/lib/zymbit/zkenv.conf)
fi

/sbin/zkunlockifs /var/lib/zymbit/key.bin.lock

err=$?
if [ ${err} -ne 0 ]
then
    /lib/cryptsetup/askpass "Zymbit key did not release LUKS key. Enter backup LUKS passphrase:"
fi
EOF

Now, rebuild the initramfs:

update-initramfs -u -k all

… and reboot!