Created
November 22, 2019 22:51
-
-
Save deeglaze/a8f0f982bc1e4d17c38cf9571d15fd37 to your computer and use it in GitHub Desktop.
Describes data in SGX sign_tool's gendata output
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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