Commit b752cc948a863e23c8a75c79f455edb97f5e74ec

Authored by Tim Gover
1 parent e6202936

tools: vcmailbox wrappers for managing 256 bit OTP private key

Showing 1 changed file with 106 additions and 0 deletions
tools/rpi-otp-private-key 0 → 100755
  1 +#!/bin/sh
  2 +
  3 +set -e
  4 +
  5 +FORCE=0
  6 +READ_KEY=""
  7 +WRITE_KEY=""
  8 +OUTPUT_BINARY=0
  9 +
  10 +die() {
  11 + echo "$@" >&2
  12 + exit 1
  13 +}
  14 +
  15 +usage() {
  16 + cat <<EOF
  17 + $(basename "$0") [-cfw] <key>
  18 +
  19 + No args - reads the current private key from OTP. These values are NOT visible via 'vcgencmd otp_dump'
  20 +
  21 + -b Output the key in binary format.
  22 + -c Reads key and exits with 1 if it is all zeros i.e. not set.
  23 + -f Force write (if OTP is non-zero).
  24 + The vcmailbox API checks that the new key is equal to the bitwise OR of the current OTP and the new key.
  25 + N.B. OTP bits can never change from 1 to 0.
  26 + -w Writes the new key to OTP memory.
  27 +
  28 + <key> is a 64 digit hex number (256 bit) e.g. to generate a 256 random number run 'openssl rand -hex 32'
  29 +
  30 + IMPORTANT: Raspberry Pi 4 and earlier revisions do not have a hardware secure key store. These OTP rows are visible
  31 + to any user in the 'video' group via vcmailbox. Therefore this functionality is only suitable for key
  32 + storage if the OS has already been restricted using the signed boot functionality.
  33 +
  34 + WARNING: Changes to OTP memory are permenant and cannot be undone.
  35 +EOF
  36 +exit 1
  37 +}
  38 +
  39 +check_key_set() {
  40 + read_key
  41 + if [ -z "$(echo "${READ_KEY}" | sed s/0//g)" ]; then
  42 + return 1
  43 + fi
  44 + return 0
  45 +}
  46 +
  47 +read_key() {
  48 + out=READ_KEY="$(vcmailbox 0x00030081 40 40 0 8 0 0 0 0 0 0 0 0)" || die "Failed to read the current key from OTP"
  49 + READ_KEY="$(echo "${out}" | sed 's/0x//g' | awk '{for(i=8;i<16;i++) printf $i; print ""}')"
  50 +}
  51 +
  52 +write_key() {
  53 + key="${1}"
  54 + # Normalize formatting and check the length
  55 + key="$(echo "${key}" | tr 'A-Z' 'a-z')"
  56 + key="$(echo "${key}" | sed 's/[^a-f0-9]//g')"
  57 + [ "$(echo -n "${key}" | wc -c)" = 64 ] || die "Invalid key parameter"
  58 +
  59 + count=0
  60 + key_params=""
  61 + while [ ${count} -lt 8 ]; do
  62 + start=$(((count * 8) + 1))
  63 + end=$((start + 7))
  64 + key_params="${key_params} 0x$(echo -n "${key}" | cut -c${start}-${end})"
  65 + count=$((count + 1))
  66 + done
  67 + vcmailbox 0x38081 40 40 0 8 ${key_params} || die "Failed to write key"
  68 + read_key
  69 + [ "${READ_KEY}" = "${key}" ] || die "Key readback check failed. ${out}"
  70 +}
  71 +
  72 +while getopts bcfhw: option; do
  73 + case "${option}" in
  74 + b) OUTPUT_BINARY=1
  75 + ;;
  76 + c)
  77 + if check_key_set; then
  78 + exit 0
  79 + fi
  80 + exit 1
  81 + ;;
  82 + f) FORCE=1
  83 + ;;
  84 + h) usage
  85 + ;;
  86 + w) WRITE_KEY="${OPTARG}"
  87 + ;;
  88 + *) echo "Unknown argument \"${option}\""
  89 + usage
  90 + ;;
  91 + esac
  92 +done
  93 +
  94 +if [ -n "${WRITE_KEY}" ]; then
  95 + if [ "${FORCE}" = 0 ] && check_key_set; then
  96 + die "Current key is non-zero. Specify -f to write anyway"
  97 + fi
  98 + write_key "${WRITE_KEY}"
  99 +else
  100 + read_key
  101 + if [ "${OUTPUT_BINARY}" = 1 ]; then
  102 + echo "${READ_KEY}" | xxd -r -p
  103 + else
  104 + echo "${READ_KEY}"
  105 + fi
  106 +fi
... ...