summaryrefslogtreecommitdiff
path: root/util/signer/bs
blob: 9e937e24a77dda24bb6ed61b40f39bd07744bbe6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
#!/bin/bash

#
# Copyright 2016 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# This script is a utility which allows to create differently signed CR50
# images from different sources.
#
set -e
set -u

progname=$(basename $0)
EC_ROOT="$(readlink -f "$(dirname $0)/../..")"

OD="/usr/bin/od"

tmpf="/tmp/bs_manifest.$$"
trap "{ if [[ -f 1.flat ]]; then rm -rf [01].flat ${tmpf} ; fi; }" EXIT

usage() {
  local rv="${1}"
  cat <<EOF

This script allows to sign CR50 RW images. By default it uses ec.RW.elf and
ec.RW_B.elf in build/cr50/RW as inputs and util/signer/ec_RW-manifest-dev.json
as the manifest, and places the newly signed images into build/cr50/ec.bin.

The only outside dependency of this script is the signing utility itself,
which is expected to be available as /usr/bin/cr50-codesigner.

The utility can be installed by running 'sudo emerge cr50-utils',

The following command line options are accepted:

  b1 - generate signature for the b1 version of the H1 chip
  elves <elf1> <elf2> - sign the supplied elf files instead of the default
        ones. Handy if the builder generated files need to be signed
  help - print this message
  hex - generate hex output instead of binary (place in 0.signed.hex and
        1.signed.hex in the local directory)
  prod - sign with prod key (no debug image will be signed)

This script also allows to sign dev images for running on prod RO. To do that
invoke this script as follows:

  H1_DEVIDS='<dev id0> <dev id1>' ${progname} [other options, if any]

where <dev id0> <dev id1> are values reported by sysinfo command in the
DEV_ID: line when run on the CR50 for which the image is built.

The same values can be obtained in the lsusb command output:

  lsusb -vd 18d1:5014 | grep -i serial

note that the lsusb reported values are in hex and need to be prefixed with
0x.

Finally, this script also allows to specify the board ID fields of the RW
headers. The fields come from the evironment variable CR50_BOARD_ID, which is
required to include three colon separated fields. The first field is a four
letter board RLZ code, the second field is board id mask in hex, no 0x prefix,
and the third field - board ID flags, again, hex, no 0x prefix.

CR50_BOARD_ID='XXYY:ffffff00:ff00' ${progname} [other options, if any]

both H1_DEVIDS and CR50_BOARD_ID can be defined independently.

EOF
  exit "${rv}"
}

# This function modifies the manifest to include device ID and board ID nodes,
# if H1_DEVIDS and CR50_BOARD_ID are defined in the environment, respectively,
tweak_manifest () {
  local sub

  # If defined, plug in dev ID nodes before the 'fuses' node.
  if [[ -z "${do_prod}" && -n "${H1_DEVIDS}" ]]; then
    echo "creating a customized DEV image for DEV IDS ${H1_DEVIDS}"
    sub=$(printf "\\\n   \"DEV_ID0\": %s,\\\n   \"DEV_ID1\": %s," ${H1_DEVIDS})
    sed -i "s/\"fuses\": {/\"fuses\": {${sub}/" "${tmpf}"
  fi

  if [[ -z "${CR50_BOARD_ID}" ]]; then
    return
  fi

  # CR50_BOARD_ID is set, let's parse it and plug in the board ID related
  # nodes into manifest before the 'fuses' node.
  local bid_params
  local rlz

  bid_params=( $(echo $CR50_BOARD_ID | sed 's/:/ /g') )
  # A very basic validity check: it needs to consist of three colon separated
  # fields.
  if [[ ${#bid_params[@]} != 3 ]]; then
    echo "Wrong board ID string \"$CR50_BOARD_ID\"}" >&2
    exit 1
  fi

  if [[ "${bid_params[0]}" == "0" ]] ; then
    rlz="0"
  elif [[ ${#bid_params[0]} == 4 ]] ; then
    # Convert 4 char board RLZ code from ASCII to hex
    rlz="0x$(echo -n ${bid_params[0]} | hexdump -ve '/1 "%02x"')"
  else
    echo "Invalid RLZ ${bid_params[0]}"
    exit 1
  fi

  # Prepare text of all three board ID related nodes
  sub="$(printf "\\\n\"board_id\": %s,\\\n" "${rlz}")"
  sub+="$(printf "\"board_id_mask\": %s,\\\n" "0x${bid_params[1]}")"
  sub+="$(printf "\"board_id_flags\": %s,\\\n" "0x${bid_params[2]}")"
  sed -i "s/\"fuses\": {/${sub}\"fuses\": {/" "${tmpf}"
}

# This function accepts two arguments, names of two binary files.
#
# It searches the first passed in file for the first 8 bytes of the second
# passed in file. The od utility is used to generate full hex dump of the
# first file (16 bytes per line) and the first 8 bytes of the second file.
#
# grep is used to check if the pattern is present in the full dump. If the
# pattern is not found, the first file is dumped again, this time with an 8
# byte offset into the file. This makes sure that if the match is present, but
# is spanning two lines of the original hex dump, it is in a single dump line
# the second time around.
find_blob_in_blob() {
  local main_blob="${1}"
  local pattern_blob="${2}"
  local pattern
  local od_options="-An -tx1"

  # Get the first 8 bytes of the pattern blob.
  pattern="$(${OD} ${od_options} -N8 "${pattern_blob}")"

  if "${OD}" ${od_options} "${main_blob}" | grep "${pattern}" > /dev/null; then
    return 0
  fi

  # Just in case pattern was wrapped in the previous od output, let's do it
  # again with an 8 bytes offset
  if "${OD}" ${od_options} -j8 "${main_blob}" |
      grep "${pattern}" > /dev/null; then
    return 0
  fi

  return 1
}

# This function accepts two arguments, names of the two elf files.
#
# The files are searched for test RMA public key patterns - x25519 or p256,
# both files are supposed to have pattern of one of these keys and not the
# other. If this holds true the function prints the public key base name. If
# not both files include the same key, or include more than one key, the
# function reports failure and exits the script.
determine_rma_key_base() {
  local base_name="${EC_ROOT}/board/cr50/rma_key_blob"
  local curve
  local curves=( "x25519" "p256" )
  local elf
  local elves=( "$1" "$2" )
  local key_file
  local mask=1
  local result=0

  for curve in ${curves[@]}; do
    key_file="${base_name}.${curve}.test"
    for elf in ${elves[@]}; do
      if find_blob_in_blob "${elf}" "${key_file}"; then
        result=$(( result | mask ))
      fi
      mask=$(( mask << 1 ))
    done
  done

  case "${result}" in
    (3)  curve="x25519";;
    (12) curve="p256";;
    (*) echo "could not determine key type in the elves" >&2
        exit 1
        ;;
  esac

  echo "${base_name}.${curve}"
}

SIGNER="cr50-codesigner"
if ! which "${SIGNER}" 2>/dev/null > /dev/null; then
  echo "${SIGNER} is not available, try running 'sudo emerge cr50-utils'" >&2
  exit 1
fi

# This is where the new signed image will be pasted into.
: ${RESULT_FILE=build/cr50/ec.bin}
TMP_RESULT_FILE="${RESULT_FILE}.tmp"

if [[ -z "${CROS_WORKON_SRCROOT}" ]]; then
  echo "${progname}: This script must run inside Chrome OS chroot" >&2
  exit 1
fi

: ${CR50_BOARD_ID=}
: ${H1_DEVIDS=}
EC_BIN_ROOT="${EC_ROOT}/util/signer"

do_hex=
do_b1=
do_prod=

# Prepare the default manifest.
cp "${EC_BIN_ROOT}/ec_RW-manifest-dev.json" "${tmpf}"

elves=( build/cr50/RW/ec.RW.elf build/cr50/RW/ec.RW_B.elf )
cd "${EC_ROOT}"
while (( $# )); do
  param="${1}"
  case "${param}" in
    (hex) do_hex='true';;
    (b1)
      do_b1='true'
      sed -i 's/\(.*FW_DEFINED_DATA_BLK0.*\): 2/\1: 0/' "${tmpf}"
      ;;
    (elves)
      if [[ (( $# < 3 )) ]]; then
        echo "two elf file names are required" >&2
        exit 1
      fi
      elves=( $2 $3 )
      shift
      shift
      ;;
    (prod)
      do_prod='true'
      ;;
    (help)
      usage 0
      ;;
    (*)
      usage 1
      ;;
  esac
  shift
done

if [[ -z "${do_hex}" && ! -f "${RESULT_FILE}" ]]; then
  echo "${RESULT_FILE} not found. Run 'make BOARD=cr50' first" >&2
  exit 1
fi

if [[ -n "${do_prod}" && -n "${do_b1}" ]]; then
  echo "can not build prod images for B1, sorry..."
  exit 1
fi

# If signing a chip factory image (version 0.0.22) do not try figuring out the
# RMA keys.
ignore_rma_keys="$(awk '
  BEGIN {count = 0};
   /"major": 0,/ {count += 1};
   /"minor": 22,/ {count += 1};
   END {{if (count == 2) {print "yes"};}}' \
        "${EC_BIN_ROOT}/ec_RW-manifest-prod.json")"

if [ "${ignore_rma_keys}" != "yes" ]; then
  rma_key_base="$(determine_rma_key_base ${elves[@]})"
else
  echo "Ignofing RMA keys for factory branch"
fi

signer_command_params=()
signer_command_params+=(--b -x ${EC_BIN_ROOT}/fuses.xml)
if [[ -z "${do_prod}" ]]; then
  signer_command_params+=(-k ${EC_BIN_ROOT}/cr50_rom0-dev-blsign.pem.pub)
else
  cp "${EC_BIN_ROOT}/ec_RW-manifest-prod.json" "${tmpf}"
  signer_command_params+=(-k ${EC_BIN_ROOT}/cr50_RW-prod.pem.pub)
  # Swap test public RMA server key with the prod version.
  if [ "${ignore_rma_keys}" != "yes" ]; then
    signer_command_params+=(-S "${rma_key_base}.test","${rma_key_base}.prod")
  fi
fi
signer_command_params+=(-j ${tmpf})

if [[ -n "${do_hex}" ]]; then
  dst_suffix='signed.hex'
else
  signer_command_params+=(--format=bin)
  dst_suffix='flat'
fi

tweak_manifest

count=0
for elf in ${elves[@]}; do
  if [[ -n "${do_prod}" ]]; then
    if strings "${elf}" | egrep -q "(DBG|SQA)/cr50"; then
      echo "Will not sign debug or SQA image with prod keys" >&2
      exit 1
    fi
  fi
  signed_file="${count}.${dst_suffix}"

  # Make sure output file is not owned by root
  touch "${signed_file}"
  command="${SIGNER} ${signer_command_params[@]} -i ${elf} -o ${signed_file}"
  if ! ${command}; then
    echo "${progname}: \"${command}\" failed" >&2
    exit 1
  fi

  if [ "${ignore_rma_keys}" != "yes" ]; then
    if find_blob_in_blob  "${signed_file}" "${rma_key_base}.test"; then
      echo "${progname}: test RMA key in the signed image!" >&2
      rm *."${dst_suffix}"
      exit 1
    fi

    if ! find_blob_in_blob "${signed_file}" "${rma_key_base}.prod"; then
      echo "${progname}: prod RMA key not in the signed image!" >&2
      rm *."${dst_suffix}"
      exit 1
    fi
  fi
  : $(( count++ ))
done

if [[ -z "${do_hex}" ]]; then
  # Full binary image is required, paste the newly signed blobs into the
  # output image, preserving it in case dd fails for whatever reason.
  cp "${RESULT_FILE}" "${TMP_RESULT_FILE}"
  dd if="0.flat" of="${TMP_RESULT_FILE}" seek=16384 bs=1 conv=notrunc
  dd if="1.flat" of="${TMP_RESULT_FILE}" seek=278528 bs=1 conv=notrunc
  rm [01].flat
  mv "${TMP_RESULT_FILE}" "${RESULT_FILE}"
fi

echo "SUCCESS!!!"