#!/sbin/openrc-run
# SPDX-License-Identifier: MIT

description="Decrypt CAAM-assisted encrypted partitions"

depend() {
	after dev-mount
	before localmount
}

error() {
	printf "%s\n" "$@" >&2
	exit 1
}

decrypt() {
	local dev="$1"
	local index offset
	case "$dev" in
	*mmcblk*p*)
		# keys are stored in $rootdev as follow
		# 0MB        <GPT header and partition table>
		# 9MB        key for part 1
		# 9MB+4k     key for part 2
		# 9MB+(n*4k) key for part n+1
		# 10MB       first partition
		index=${dev##*p}
		index=$((index-1))
		offset="$(((9*1024 + index*4)*1024))"
		;;
	*) error "LUKS only supported on mmcblk*p* partitions (got $dev)" ;;
	esac

	mkdir -p /run/caam
	local KEYFILE=/run/caam/lukskey
	# use unshared tmpfs to not leak key too much
	# key is:
	# - 112 bytes of caam black key
	# - 16 bytes of iv followed by rest of key
	unshare -m sh -c "mount -t tmpfs tmpfs /run/caam \
		 && dd if=$rootdev of=$KEYFILE.mmc bs=4k count=1 status=none \
			iflag=skip_bytes skip=$offset \
		&& dd if=$KEYFILE.mmc of=$KEYFILE.bb bs=112 count=1 status=none \
		&& dd if=$KEYFILE.mmc of=$KEYFILE.enc bs=4k status=none \
			iflag=skip_bytes skip=112 \
		&& caam-decrypt $KEYFILE.bb AES-256-CBC $KEYFILE.enc $KEYFILE.luks \
		&& cryptsetup luksOpen --key-file $KEYFILE.luks \
			--allow-discards $dev ${dev##*/}" \
		|| error "Could not decrypt $dev"
}

start() {
	local part parts="" partdev rootdev

	command -v cryptsetup > /dev/null || return 0
	command -v caam-decrypt > /dev/null || return 0

	# swupdate -g is not reliable early boot
	# (/proc/self/mountinfo contains /dev/root instead of proper dev)
	rootdev=$(sed -ne 's/.*root=\([^ ]*\).*/\1/p' < /proc/cmdline)
	[ -e "$rootdev" ] || rootdev="$(findfs "$rootdev")"
	partdev="${rootdev%[12]}"
	rootdev="${partdev%p}"
	[ -e "$rootdev" ] || error "Could not find rootfs"
	for part in "${partdev}3" "${partdev}4" "${partdev}5"; do
		cryptsetup isLuks "$part" >/dev/null 2>&1 \
			&& parts="$parts $part"
	done

	[ -z "$parts" ] && return 0

	ebegin "Decrypting encrypted secondary partitions"

	for part in $parts; do
		decrypt "$part"
	done

	eend 0
}
