bug_report template: Be more precise about the UART pins
[rpi-eeprom.git] / tools / rpi-otp-private-key
blob5d5a10f68d0238c805dc1f2df24a4056fbd7afe8
1 #!/bin/sh
3 set -e
5 FORCE=0
6 READ_KEY=""
7 WRITE_KEY=""
8 OUTPUT_BINARY=0
9 ROW_COUNT=8
10 ROW_OFFSET=0
12 die() {
13 echo "$@" >&2
14 exit 1
17 usage() {
18 cat <<EOF
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.
40 EOF
41 exit 1
44 check_key_set() {
45 read_key
46 if [ -z "$(echo "${READ_KEY}" | sed s/0//g)" ]; then
47 return 1
49 return 0
52 read_key() {
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 ""}')"
57 write_key() {
58 key="${1}"
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"
64 count=0
65 key_params=""
66 while [ ${count} -lt $ROW_COUNT ]; do
67 start=$(((count * 8) + 1))
68 end=$((start + 7))
69 key_params="${key_params} 0x$(echo -n "${key}" | cut -c${start}-${end})"
70 count=$((count + 1))
71 done
73 if [ "${YES}" = 0 ] && [ -t 0 ]; then
74 echo "Write ${key} of $ROW_COUNT words to OTP starting at word offset $ROW_OFFSET?"
75 echo
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."
79 read -r confirm
80 if [ "${confirm}" != "YES" ]; then
81 echo "Cancelled"
82 exit
86 vcmailbox 0x38081 $((8 + ROW_COUNT * 4)) $((8 + ROW_COUNT * 4)) $ROW_OFFSET $ROW_COUNT ${key_params} || die "Failed to write key"
87 read_key
88 [ "${READ_KEY}" = "${key}" ] || die "Key readback check failed. ${out}"
91 YES=0
92 while getopts bcfhw:yl:o: option; do
93 case "${option}" in
94 b) OUTPUT_BINARY=1
97 if check_key_set; then
98 exit 0
100 exit 1
102 f) FORCE=1
104 h) usage
106 w) WRITE_KEY="${OPTARG}"
108 y) YES=1
110 l) ROW_COUNT="${OPTARG}"
112 o) ROW_OFFSET="${OPTARG}"
114 *) echo "Unknown argument \"${option}\""
115 usage
117 esac
118 done
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/.*://')"
126 else
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
133 MAX_ROW_COUNT=8
134 elif [ $(((0x$BOARD_INFO >> 12) & 15)) = 4 ]; then
135 MAX_ROW_COUNT=16
136 else
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
146 die "Length too big"
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
155 die "Offset too big"
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}"
169 else
170 read_key
171 if [ "${OUTPUT_BINARY}" = 1 ]; then
172 echo "${READ_KEY}" | xxd -r -p
173 else
174 echo "${READ_KEY}"