Skip to content

Commit 0ebda77

Browse files
committed
tools: Add secure-boot related scripts to tools
Copy the tools from usbboot. The next step is for usbboot to include rpi-eeprom as a git submodule to de-duplicate EEPROM images and tools.
1 parent b9fcc9d commit 0ebda77

File tree

2 files changed

+173
-0
lines changed

2 files changed

+173
-0
lines changed

tools/rpi-bootloader-key-convert

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
import struct
5+
import sys
6+
7+
from Cryptodome.PublicKey import RSA
8+
9+
def bintopem(infile, outf):
10+
f = open(infile, 'rb')
11+
arr = f.read(264)
12+
13+
n = int.from_bytes(struct.unpack_from("256B", arr, 0), 'little')
14+
e = struct.unpack_from("<Q", arr, 256)[0]
15+
pubkey = RSA.construct((n, e))
16+
outf.write(pubkey.exportKey())
17+
18+
def pemtobin(infile, outf):
19+
key = RSA.importKey(open(infile, 'r').read())
20+
21+
if key.size_in_bits() != 2048:
22+
raise Exception("RSA key size must be 2048")
23+
24+
# Extract the public key componenet n,e and store as little endian
25+
outf.write(key.n.to_bytes(256, byteorder='little'))
26+
outf.write(key.e.to_bytes(8, byteorder='little'))
27+
28+
def main():
29+
parser = argparse.ArgumentParser('Converts RSA keys between PEM format and the raw binary format used by the Raspberry Pi 4 bootloader')
30+
parser.add_argument('input', nargs='+')
31+
parser.add_argument('--inform', default="pem")
32+
parser.add_argument('--output', required=False)
33+
34+
args = parser.parse_args()
35+
36+
if args.output:
37+
outf = open(args.output, 'wb')
38+
else:
39+
outf = sys.stdout.buffer
40+
41+
if args.inform == "pem":
42+
pemtobin(args.input[0], outf)
43+
elif args.inform == "bin":
44+
bintopem(args.input[0], outf)
45+
else:
46+
raise Exception("Unknown format %s" % args.inform)
47+
48+
if __name__ == '__main__':
49+
main()

tools/rpi-otp-private-key

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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") [-cfwy] <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+
-y Skip the confirmation prompt when writing to OTP.
28+
29+
<key> is a 64 digit hex number (256 bit) e.g. to generate a 256 random number run 'openssl rand -hex 32'
30+
31+
IMPORTANT: Raspberry Pi 4 and earlier revisions do not have a hardware secure key store. These OTP rows are visible
32+
to any user in the 'video' group via vcmailbox. Therefore this functionality is only suitable for key
33+
storage if the OS has already been restricted using the signed boot functionality.
34+
35+
WARNING: Changes to OTP memory are permanent and cannot be undone.
36+
EOF
37+
exit 1
38+
}
39+
40+
check_key_set() {
41+
read_key
42+
if [ -z "$(echo "${READ_KEY}" | sed s/0//g)" ]; then
43+
return 1
44+
fi
45+
return 0
46+
}
47+
48+
read_key() {
49+
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"
50+
READ_KEY="$(echo "${out}" | sed 's/0x//g' | awk '{for(i=8;i<16;i++) printf $i; print ""}')"
51+
}
52+
53+
write_key() {
54+
key="${1}"
55+
# Normalize formatting and check the length
56+
key="$(echo "${key}" | tr 'A-Z' 'a-z')"
57+
key="$(echo "${key}" | sed 's/[^a-f0-9]//g')"
58+
[ "$(echo -n "${key}" | wc -c)" = 64 ] || die "Invalid key parameter"
59+
60+
count=0
61+
key_params=""
62+
while [ ${count} -lt 8 ]; do
63+
start=$(((count * 8) + 1))
64+
end=$((start + 7))
65+
key_params="${key_params} 0x$(echo -n "${key}" | cut -c${start}-${end})"
66+
count=$((count + 1))
67+
done
68+
69+
if [ "${YES}" = 0 ] && [ -t 0 ]; then
70+
echo "Write ${key} to OTP?"
71+
echo
72+
echo "WARNING: Updates to OTP registers are permanent and cannot be undone."
73+
74+
echo "Type YES (in upper case) to continue or press return to exit."
75+
read -r confirm
76+
if [ "${confirm}" != "YES" ]; then
77+
echo "Cancelled"
78+
exit
79+
fi
80+
fi
81+
82+
vcmailbox 0x38081 40 40 0 8 ${key_params} || die "Failed to write key"
83+
read_key
84+
[ "${READ_KEY}" = "${key}" ] || die "Key readback check failed. ${out}"
85+
}
86+
87+
YES=0
88+
while getopts bcfhw:y option; do
89+
case "${option}" in
90+
b) OUTPUT_BINARY=1
91+
;;
92+
c)
93+
if check_key_set; then
94+
exit 0
95+
fi
96+
exit 1
97+
;;
98+
f) FORCE=1
99+
;;
100+
h) usage
101+
;;
102+
w) WRITE_KEY="${OPTARG}"
103+
;;
104+
y) YES=1
105+
;;
106+
*) echo "Unknown argument \"${option}\""
107+
usage
108+
;;
109+
esac
110+
done
111+
112+
if [ -n "${WRITE_KEY}" ]; then
113+
if [ "${FORCE}" = 0 ] && check_key_set; then
114+
die "Current key is non-zero. Specify -f to write anyway"
115+
fi
116+
write_key "${WRITE_KEY}"
117+
else
118+
read_key
119+
if [ "${OUTPUT_BINARY}" = 1 ]; then
120+
echo "${READ_KEY}" | xxd -r -p
121+
else
122+
echo "${READ_KEY}"
123+
fi
124+
fi

0 commit comments

Comments
 (0)