19 $(basename "$0") [-cfwy] <key>
21 No args - reads the current private key from OTP. These values are NOT visible via 'vcgencmd otp_dump'.
23 -b Output the key in binary format.
24 -c Reads key and exits with 1 if it is all zeros i.e. not set.
25 -f Force write (if OTP is non-zero).
26 The vcmailbox API checks that the new key is equal to the bitwise OR of the current OTP and the new key.
27 N.B. OTP bits can never change from 1 to 0.
28 -w Writes the new key to OTP memory.
29 -y Skip the confirmation prompt when writing to OTP.
30 -l Specify key length in words. Defaults to 8 words (32 bytes). Pi 5 supports up to 16 words (64 bytes).
31 -o word Offset into the keystore to use, e.g. 0-7 for Pi 4, 0-15 for Pi 5. Defaults to zero.
33 <key> is usually a 64 digit hex number (256 bit) e.g. to generate a 256 random number run 'openssl rand -hex 32'
35 IMPORTANT: Raspberry Pi 5 and earlier revisions do not have a hardware secure key store. These OTP rows are visible
36 to any user in the 'video' group via vcmailbox. Therefore this functionality is only suitable for key
37 storage if the OS has already been restricted using the signed boot functionality.
39 WARNING: Changes to OTP memory are permanent and cannot be undone.
46 if [ -z "$(echo "${READ_KEY}" | sed s/0//g)" ]; then
53 out
=READ_KEY
="$(vcmailbox 0x00030081 $((8 + ROW_COUNT * 4)) $((8 + ROW_COUNT * 4)) $ROW_OFFSET $ROW_COUNT 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)" || die
"Failed to read the current key from OTP"
54 READ_KEY
="$(echo "${out}" | sed 's/0x//g' | awk -v last=$((8 + ROW_COUNT)) '{for(i=8;i<last;i++) printf $i; print ""}')"
59 # Normalize formatting and check the length
60 key
="$(echo "${key}" | tr 'A-Z' 'a-z')"
61 key
="$(echo "${key}" | sed 's/[^a-f0-9]//g')"
62 [ "$(echo -n "${key}" | wc -c)" = $
((ROW_COUNT
* 4 * 2)) ] || die
"Invalid key parameter"
66 while [ ${count} -lt $ROW_COUNT ]; do
67 start
=$
(((count
* 8) + 1))
69 key_params
="${key_params} 0x$(echo -n "${key}" | cut -c${start}-${end})"
73 if [ "${YES}" = 0 ] && [ -t 0 ]; then
74 echo "Write ${key} of $ROW_COUNT words to OTP starting at word offset $ROW_OFFSET?"
76 echo "WARNING: Updates to OTP registers are permanent and cannot be undone."
78 echo "Type YES (in upper case) to continue or press return to exit."
80 if [ "${confirm}" != "YES" ]; then
86 vcmailbox
0x38081 $
((8 + ROW_COUNT
* 4)) $
((8 + ROW_COUNT
* 4)) $ROW_OFFSET $ROW_COUNT ${key_params} || die
"Failed to write key"
88 [ "${READ_KEY}" = "${key}" ] || die "Key readback check failed. ${out}"
92 while getopts bcfhw
:yl
:o
: option
; do
97 if check_key_set
; then
106 w
) WRITE_KEY
="${OPTARG}"
110 l
) ROW_COUNT
="${OPTARG}"
112 o
) ROW_OFFSET
="${OPTARG}"
114 *) echo "Unknown argument \"${option}\""
120 if [ -f "/sys/firmware/devicetree/base/system/linux,revision" ]; then
121 BOARD_INFO
="$(od -v -An -t x1 /sys/firmware/devicetree/base/system/linux,revision | tr -d ' \n')"
122 elif grep -q Revision
/proc
/cpuinfo
; then
123 BOARD_INFO
="$(sed -n '/^Revision/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo)"
124 elif command -v vcgencmd
> /dev
/null
; then
125 BOARD_INFO
="$(vcgencmd otp_dump | grep '30:' | sed 's/.*://')"
127 die
"No Raspberry Pi board info found"
129 if [ $
(((0x
$BOARD_INFO >> 23) & 1)) = 0 ]; then
130 die
"Chip not supported"
132 if [ $
(((0x
$BOARD_INFO >> 12) & 15)) = 3 ]; then
134 elif [ $
(((0x
$BOARD_INFO >> 12) & 15)) = 4 ]; then
137 die
"Chip not supported"
139 if [ -z "$ROW_COUNT" ] ||
[ "$ROW_COUNT" -ne "$ROW_COUNT" ] 2>/dev
/null
; then
140 die
"Key length not a number"
142 if [ $ROW_COUNT -lt 1 ]; then
143 die
"Length too small"
145 if [ $ROW_COUNT -gt $MAX_ROW_COUNT ]; then
148 if [ -z "$ROW_OFFSET" ] ||
[ "$ROW_OFFSET" -ne "$ROW_OFFSET" ] 2>/dev
/null
; then
149 die
"Offset is not a number"
151 if [ $ROW_OFFSET -lt 0 ]; then
152 die
"Offset too small"
154 if [ ${ROW_OFFSET} -gt $
((MAX_ROW_COUNT
- ROW_COUNT
)) ]; then
157 if [ -z $
(which vcmailbox
) ]; then
158 die
"vcmailbox command missing"
160 if [ -z $
(which xxd
) ] && [ "$OUTPUT_BINARY" -eq "1" ]; then
161 die
"xxd command missing"
164 if [ -n "${WRITE_KEY}" ]; then
165 if [ "${FORCE}" = 0 ] && check_key_set
; then
166 die
"Current key is non-zero. Specify -f to write anyway"
168 write_key
"${WRITE_KEY}"
171 if [ "${OUTPUT_BINARY}" = 1 ]; then
172 echo "${READ_KEY}" | xxd
-r -p