Skip to content

Instantly share code, notes, and snippets.

@deeglaze
Created November 22, 2019 22:51
Show Gist options
  • Save deeglaze/a8f0f982bc1e4d17c38cf9571d15fd37 to your computer and use it in GitHub Desktop.
Save deeglaze/a8f0f982bc1e4d17c38cf9571d15fd37 to your computer and use it in GitHub Desktop.
Describes data in SGX sign_tool's gendata output
#!/bin/bash
# A script for explaining different parts of an enclave's signing material
function usage() {
echo >&2
echo "Usage: $(basename "$0") [flags] <path_to_signing_material.dat>" >&2
echo >&2
echo " -c,--color=MODE: MODE is one of always, never, auto [default]." >&2
echo " If in a tty, --color=auto produces color codes." >&2
echo " Color codes are never used if --color=never." >&2
echo " Color codes are always used if --color=always." >&2
echo " -j,--json: Produces a JSON literal with a hex string breakdown" >&2
echo " of the different regions of enclave signing material." >&2
echo >&2
}
# Binary format split up and descriped in linux_sgx's arch.h.
# A region is bytes left inclusive, right exclusive of REGIONS[i]..REGIONS[i+1].
# The final 256 is an upper bound sentinel.
readonly REGIONS=(
0 12 16 20 24 40 44 128 132 136 140 156 172 188 220 236 252 254 256
)
# Field names for each chunk of bytes.
readonly REGION_NAMES=(
"header" "type" "module_vendor" "date" "header2" "hw_version" "reserved"
"misc_select" "misc_mask" "reserved2" "isv_family_id" "attributes"
"attribute_mask" "enclave_hash" "reserved3" "isvext_prod_id" "isv_prod_id"
"isv_svn"
)
# Use 18 different alternating contrast colors for highlighting the different
# regions of the signing material.
# See the table here: https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit
# Background colors
readonly BGCOLORS256=(
20 205 30 215 41 226 51 231 46 221 36 211 26 201 90 196 129 100
)
# Foreground colors
readonly FGCOLORS256=(
15 15 15 0 0 0 0 0 0 0 15 15 15 15 15 15 15 15
)
readonly BGCOLORS8=(
1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4
)
# Foreground colors
readonly FGCOLORS8=(
7 0 0 7 7 0 7 7 0 0 7 7 0 7 7 0 0 7
)
# Return the index of REGIONS that a byte number sits in.
function byte_region_index() {
local readonly byte_number="$1"
local readonly region_count="${#REGIONS[@]}"
for r in $(seq 0 1 $((region_count - 2))); do
# If the byte_number is less than the region's starting byte, return r.
if [[ "${byte_number}" -lt "${REGIONS[$((r + 1))]}" ]]; then
echo "${r}"
return 0
fi
done
return 1
}
function C() {
local readonly color="$1"
local readonly last_color="$2"
if ([[ -z "${last_color}" ]] || [[ "${color}" -ne "${last_color}" ]]) &&
[[ -n "${TPUT}" ]]; then
if [[ ${color_count} -lt 256 ]]; then
"${TPUT}" setaf "${FGCOLORS8["$1"]}"
"${TPUT}" setab "${BGCOLORS8["$1"]}"
else
"${TPUT}" setaf "${FGCOLORS256["$1"]}"
"${TPUT}" setab "${BGCOLORS256["$1"]}"
fi
fi
}
function reset_color() {
if [[ -n "${TPUT}" ]]; then
"${TPUT}" sgr0
fi
}
explain_count=0 # Number of times explain has been called, to change colors.
function explain() {
local readonly header="$1"
local readonly description="$2"
if [[ -n "${TPUT}" ]]; then
"${TPUT}" bold; C "${explain_count}"; echo -n "${header}"; "${TPUT}" sgr0
echo ": ${description}"
else
echo "${header}: ${description}"
fi
explain_count=$((explain_count + 1))
}
function colorize_string() {
local readonly line="$1"
local readonly line_length="${#line}"
local byte_number="$2" # The byte number of ${line:0:1}
local readonly byte_stride="$3" # How many characters constitude a "byte"?
# Insert a space every n characters. Must be a multiple of byte_stride.
local readonly space_stride="$4"
local index=
local readonly end=$((line_length - byte_stride))
local color=
local last_color=
for index in $(seq 0 "${byte_stride}" "${end}"); do
if [[ $((index * space_stride)) -ne 0 ]] &&
[[ $((index % space_stride)) -eq 0 ]]; then
echo -n " "
fi
byte="${line:${index}:${byte_stride}}"
color=$(byte_region_index $((byte_number + (index/byte_stride))))
C "${color}" "${last_color}" || {
reset_color
echo "Color error" >&2
exit 1
}
last_color="${color}"
echo -n "${byte}"
done
reset_color
}
# Expected input format:
# 00000000: 0600 0000 e100 0000 0000 0100 0000 0000 ................
# 00000010: 0000 0000 2111 1920 0101 0000 6000 0000 ....!.. ....`...
# 00000020: 6000 0000 0100 0000 0000 0000 0000 0000 `...............
# 00000030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
# 00000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
# 00000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................
# 00000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................
# 00000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................
# 00000080: 0000 0000 ffff ffff 0000 0000 0000 0000 ................
# 00000090: 0000 0000 0000 0000 0000 0000 0400 0000 ................
# 000000a0: 0000 0000 0300 0000 0000 0000 fdff ffff ................
# 000000b0: ffff ffff 1bff ffff ffff ffff ffea 1b2b ...............+
# 000000c0: c324 2d67 f0f1 75c3 17e5 b8d2 6443 784d .$-g..u.....dCxM
# 000000d0: 9be4 6c50 be8e 6e4d cec8 0668 0000 0000 ..lP..nM...h....
# 000000e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
# 000000f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
function color_hex() {
local byte_number=0
local hex_part=
local str_part=
local chex=
local cstr=
while read xxdline; do
address_number="${xxdline:0:10}"
# 7 spaces + 8 couplets of bytes written as 2 characters each: 7 + 32
hex_part="${xxdline:10:39}"
hex_part="${hex_part// /}" # Remove spaces.
str_part="${xxdline:51:17}"
echo -n "${address_number}"
colorize_string "${hex_part}" "${byte_number}" 2 4
echo -n " "
colorize_string "${str_part}" "${byte_number}" 1 0
echo
byte_number=$((byte_number + 16))
done < <("${XXD}" "${signing_material}")
reset_color
explain "Header" "Should be 0x06000000e100000000000100"
explain "Type" "bit 31: 0 = prod, 1 = debug; Bit 30-0: Must be zero"
explain "Vendor" "Intel=0x8086, ISV=0x0000"
explain "Date" "Build date as yyyymmdd"
explain "Header2" "Must be 0x01010000600000006000000001000000"
explain "HW_VERSION" "Nonzero for only for launch enclaves"
explain "Reserved" "Must be 0"
explain "MISC_SELECT" "The MISCSELECT that must be set"
explain "MISC_MASK" "Mask of MISCSELECT to enforce"
explain "Reserved" "Must be 0"
explain "ISVFAMILYID" "ISV assigned family identifier"
explain "Attributes" "Enclave Attributes that must be set"
explain "Attribute mask" "Mask of Attributes to Enforce"
explain "MRENCLAVE" "The enclave code identity"
explain "Reserved" "Must be 0"
explain "ISVEXTPRODID" "ISV assigned extended product identifier"
explain "ISPRODID" "ISV assigned product identifier"
explain "ISVSVN" "ISV assigned security version number"
}
function print_json() {
local byte_number=0
local hex_part=
local region=
local last_region=0
local chunk=
echo "{"
while read xxdline; do
hex_part="${xxdline:10:39}"
hex_part="${hex_part// /}" # Remove spaces.
for index in $(seq 0 2 30); do
byte="${hex_part:${index}:2}"
region=$(byte_region_index $((byte_number + (index/2))))
if [[ "${region}" -ne "${last_region}" ]]; then
echo " \"${REGION_NAMES[${last_region}]}\": \"${chunk}\","
chunk=
fi
last_region="${region}"
chunk="${chunk}${byte}"
done
byte_number=$((byte_number + 16))
done < <("${XXD}" "${signing_material}")
echo " \"${REGION_NAMES[-1]}\": \"${chunk}\""
echo "}"
}
color_mode="auto"
json=
getopt_gnu=$(getopt -T)
PARSED=
if [[ $? -ne 4 ]] && [[ -n "${getopt_gnu}" ]]; then
# Not GNU getopt. Translate long form flags to short form.
args=$(echo "$@" | sed -e 's/--color(=)?/-c/' -e 's/--json/-j/')
PARSED=$(getopt c:j "${args}")
if [ $? != 0 ]; then
usage
exit 1
fi
else
PARSED=$(getopt -o c:j --long color:,json -n "$(basename "$0")" -- "$@")
fi
eval set -- "${PARSED}"
while true; do
case "$1" in
-c|--color) color_mode="$2"; shift 2 ;;
-j|--json) json="$1"; shift ;;
--) shift ; break ;;
*) usage; exit 1 ;;
esac
done
declare -r color_mode
readonly signing_material="$1"
# Error-checking inputs
readonly lcolor=$(echo "${color_mode}" | tr '[:upper:]' '[:lower:]')
use_color=
if ([[ "${lcolor}" = "auto" ]] && [[ -t 0 ]]) || [[ "${lcolor}" = always ]]; then
use_color=1
elif [[ "${lcolor}" != "never" ]]; then
echo "Expected --color to be one of always, never, auto. Got ${color_mode}" >&2
exit 1
fi
declare -r use_color
if ([[ -n "${use_color}" ]] && [[ ! $(which tput) ]]) ||
[[ ! $(which xxd) ]]; then
echo "This script depends on tput and xxd (apt install ncurses-bin xxd)" >&2
exit 1
fi
readonly XXD="$(which xxd)"
TPUT=
if [[ -n "${use_color}" ]]; then
TPUT="$(which tput)"
color_count=$("${TPUT}" colors)
if [[ ${color_count} -lt 8 ]]; then
echo "Expected color resolution >= 8 colors. Got ${color_count}" >&2
exit 1
fi
fi
if [[ -z "${signing_material}" ]]; then
usage
exit 1
fi
readonly file_size=$(ls -l "${signing_material}" | tr -s " " | cut -d$' ' -f5)
if [[ "${file_size}" -ne 256 ]]; then
echo "Signing material file is not the expect size (256 bytes)." >&2
exit 1
fi
if [[ -n "${json}" ]]; then
print_json
else
color_hex
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment