[RFC] example request-key helper script

Chris Leech cleech at redhat.com
Wed Apr 22 16:10:23 PDT 2026


This is a prototype of a request-key helper to instantiate a "PSK request" key.

Assisted-by: Claude:claude-sonnet-4.5
---
#!/usr/bin/bash
#
# NVMe PSK Provider
#
# This script is called by /sbin/request-key when the kernel requests
# NVMe TLS PSKs via the nvme-psk-request key type.
#
# configure /etc/request-key.conf with an entry like the following:
# create  nvme-psk-request  *  *  /usr/libexec/nvme-psk-provider %k %d %c %S

KEY_ID=$1
DESCRIPTION=$2
CALLOUT_INFO=$3
KEYRING=$4

exec > >(logger -t "key.nvme-psk") 2>&1
echo "$0: $@"

# Parse callout info
eval $(echo "$CALLOUT_INFO" | tr ' ' '\n' | grep '=')

KEYFILE="/etc/nvme/tls-keys"
HOST_NQN=${hostnqn}
SUBSYS_NQN=${subnqn}

if [ -z "$KEYFILE" ] || [ -z "$HOST_NQN" ] || [ -z "$SUBSYS_NQN" ]; then
    echo "Error: Missing required parameters"
    keyctl negate "$KEY_ID" 1 "$KEYRING"
    exit 1
fi

if [ ! -f "$KEYFILE" ]; then
    echo "Error: Keyfile '$KEYFILE' not found"
    keyctl negate "$KEY_ID" 1 "$KEYRING"
    exit 1
fi

# Temporary file for binary payload
PAYLOAD_FILE=$(mktemp)
trap "rm -f $PAYLOAD_FILE" EXIT

KEY_COUNT=0

# Search for all matching entries in keyfile
while IFS= read -r line; do
    # Skip empty lines and comments
    [ -z "$line" ] && continue
    [[ "$line" =~ ^#.* ]] && continue

    # Extract the last space-separated field (the encoded key)
    encoded_key="${line##* }"
    # Extract everything before the last space (the description)
    description="${line% *}"

    # Parse description: NVMe<ver><type>0<hmac> <hostnqn> <subsysnqn> <digest>
    if [[ "$description" =~ ^NVMe([0-9])([RG])([0-9][0-9])[[:space:]]+([^[:space:]]+)[[:space:]]+([^[:space:]]+)[[:space:]]+([^[:space:]]+)$ ]]; then
        ver="${BASH_REMATCH[1]}"
        type="${BASH_REMATCH[2]}"
        hmac="${BASH_REMATCH[3]}"
        desc_hostnqn="${BASH_REMATCH[4]}"
        desc_subsysnqn="${BASH_REMATCH[5]}"
        desc_digest="${BASH_REMATCH[6]}"

        # Only process Retained keys (not Generated)
        if [ "$type" != "R" ]; then
            continue
        fi

        # Check if this matches our search criteria
        if [ "$desc_hostnqn" = "$HOST_NQN" ] && [ "$desc_subsysnqn" = "$SUBSYS_NQN" ]; then
            # Validate the encoded key format: NVMeTLSkey-1:xx:...:
            if [[ "$encoded_key" =~ ^NVMeTLSkey-1:([0-9][0-9]):(.+):$ ]]; then
                key_hmac="${BASH_REMATCH[1]}"
                base64_data="${BASH_REMATCH[2]}"

                echo "Found matching PSK: version=$ver type=$type hmac=$hmac"

                # Decode base64
                tmpfile=$(mktemp)
                echo -n "$base64_data" | base64 -d > "$tmpfile"
                decoded_len=$(stat -c%s "$tmpfile")

                if [ "$decoded_len" -lt 36 ]; then
                    echo "Warning: Decoded data too short ($decoded_len bytes), skipping"
                    rm -f "$tmpfile"
                    continue
                fi

                # Key length is decoded_len - 4 (CRC is last 4 bytes)
                key_len=$((decoded_len - 4))

                # Verify CRC32 (optional - nvme-cli already validated on import)
                # Extract key data (without CRC)
                key_data=$(dd if="$tmpfile" bs=1 count="$key_len" 2>/dev/null)

                # Decode digest from base64
                digest_data=$(echo -n "$desc_digest" | base64 -d)
                digest_len=$(echo -n "$digest_data" | wc -c)

                # Pack binary: hmac(1) + key_len(1) + digest_len(1) + key_data[key_len] + digest[digest_len]
                # Convert hmac from decimal string to hex byte
                printf "\\x$(printf '%02x' $key_hmac)" >> "$PAYLOAD_FILE"
                printf "\\x$(printf '%02x' $key_len)" >> "$PAYLOAD_FILE"
                printf "\\x$(printf '%02x' $digest_len)" >> "$PAYLOAD_FILE"
                echo -n "$key_data" >> "$PAYLOAD_FILE"
                echo -n "$digest_data" >> "$PAYLOAD_FILE"

                KEY_COUNT=$((KEY_COUNT + 1))
                rm -f "$tmpfile"
            else
                echo "Warning: Invalid encoded key format, skipping"
            fi
        fi
    fi
done < "$KEYFILE"

if [ "$KEY_COUNT" -eq 0 ]; then
    echo "Error: No matching keys found for host-nqn '$HOST_NQN' and subsystem-nqn '$SUBSYS_NQN'"
    keyctl negate "$KEY_ID" 1 "$KEYRING"
    exit 1
fi

# Instantiate the nvme-psk-request key with binary payload
echo keyctl instantiate "$KEY_ID" @s "$KEYRING" < "$PAYLOAD_FILE"
keyctl pinstantiate "$KEY_ID" "$KEYRING" < "$PAYLOAD_FILE"
if [ $? -eq 0 ]; then
    echo "Successfully instantiated $KEY_COUNT PSK(s)"
    exit 0
else
    echo "Error: Failed to instantiate request key"
    keyctl negate "$KEY_ID" 1 "$KEYRING"
    exit 1
fi




More information about the Linux-nvme mailing list