#!/bin/bash -eux
# Copyright 2014 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.

me=${0##*/}
TMP="$me.tmp"

# Work in scratch directory
cd "$OUTDIR"

# Helper utility to modify binary blobs
REPLACE="${BUILD_RUN}/tests/futility/binary_editor"

# First, let's test the basic functionality

# For simplicity, we'll use the same size for all properties.
"${FUTILITY}" gbb -c 16,0x10,16,0x10 "${TMP}.blob"

# Flags
"${FUTILITY}" gbb -s --flags=0xdeadbeef "${TMP}.blob"
"${FUTILITY}" gbb -g --flags "${TMP}.blob" | grep -i 0xdeadbeef

# HWID length should include the terminating null - this is too long
if "${FUTILITY}" gbb -s --hwid="0123456789ABCDEF" "${TMP}.blob"; then
  false;
fi
# This works
"${FUTILITY}" gbb -s --hwid="0123456789ABCDE" "${TMP}.blob"
# Read it back?
"${FUTILITY}" gbb -g "${TMP}.blob" | grep "0123456789ABCDE"

# Same kind of tests for the other fields, but they need binary files.

# too long
dd if=/dev/urandom bs=17 count=1 of="${TMP}.data1.toolong"
dd if=/dev/urandom bs=17 count=1 of="${TMP}.data2.toolong"
if "${FUTILITY}" gbb -s --rootkey     "${TMP}.data1.toolong" "${TMP}.blob";
  then false; fi
if "${FUTILITY}" gbb -s --recoverykey "${TMP}.data2.toolong" "${TMP}.blob";
  then false; fi

# shorter than max should be okay, though
dd if=/dev/urandom bs=10 count=1 of="${TMP}.data1.short"
dd if=/dev/urandom bs=10 count=1 of="${TMP}.data2.short"
"${FUTILITY}" gbb -s \
  --rootkey     "${TMP}.data1.short" \
  --recoverykey "${TMP}.data2.short" "${TMP}.blob"
# read 'em back
"${FUTILITY}" gbb -g \
  --rootkey     "${TMP}.read1" \
  --recoverykey "${TMP}.read2" "${TMP}.blob"
# Verify (but remember, it's short)
cmp -n 10 "${TMP}.data1.short" "${TMP}.read1"
cmp -n 10 "${TMP}.data2.short" "${TMP}.read2"

# Okay
dd if=/dev/urandom bs=16 count=1 of="${TMP}.data1"
dd if=/dev/urandom bs=16 count=1 of="${TMP}.data2"
dd if=/dev/urandom bs=16 count=1 of="${TMP}.data3"
"${FUTILITY}" gbb -s --rootkey     "${TMP}.data1" "${TMP}.blob"
"${FUTILITY}" gbb -s --recoverykey "${TMP}.data2" "${TMP}.blob"

# Read 'em back.
"${FUTILITY}" gbb -g --rootkey     "${TMP}.read1" "${TMP}.blob"
"${FUTILITY}" gbb -g --recoverykey "${TMP}.read2" "${TMP}.blob"
# Verify
cmp "${TMP}.data1" "${TMP}.read1"
cmp "${TMP}.data2" "${TMP}.read2"


# Okay, creating GBB blobs seems to work. Now let's make sure that corrupted
# blobs are rejected.

# Danger Will Robinson! We assume that ${TMP}.blob has this binary struct:
#
# Field                 Offset  Value
#
# signature:            0x0000  $GBB
# major_version:        0x0004  0x0001
# minor_version:        0x0006  0x0001
# header_size:          0x0008  0x00000080
# flags:                0x000c  0xdeadbeef
# hwid_offset:          0x0010  0x00000080
# hwid_size:            0x0014  0x00000010
# rootkey_offset:       0x0018  0x00000090
# rootkey_size:         0x001c  0x00000010
# bmpfv_offset:         0x0020  0x000000a0
# bmpfv_size:           0x0024  0x00000010
# recovery_key_offset:  0x0028  0x000000b0
# recovery_key_size:    0x002c  0x00000010
# pad:                  0x0030  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
#                       0x0040  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
#                       0x0050  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
#                       0x0060  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
#                       0x0070  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# (HWID)                0x0080  30 31 32 33 34 35 36 37 38 39 41 42 43 44 45 00
# (rootkey)             0x0090  xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
# (bmpfv)               0x00a0  xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
# (recovery_key)        0x00b0  xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
#                       0x00c0  <EOF>
#

# bad major_version
"${REPLACE}" 0x4 2 < "${TMP}.blob" > "${TMP}.blob.bad"
if "${FUTILITY}" gbb "${TMP}.blob.bad"; then false; fi

# header size too large
"${REPLACE}" 0x8 0x81 < "${TMP}.blob" > "${TMP}.blob.bad"
if "${FUTILITY}" gbb "${TMP}.blob.bad"; then false; fi

# header size too small
"${REPLACE}" 0x8 0x7f < "${TMP}.blob" > "${TMP}.blob.bad"
if "${FUTILITY}" gbb "${TMP}.blob.bad"; then false; fi

# HWID not null-terminated is invalid
"${REPLACE}" 0x8f 0x41 < "${TMP}.blob" > "${TMP}.blob.bad"
if "${FUTILITY}" gbb "${TMP}.blob.bad"; then false; fi

# HWID of length zero is okay
"${REPLACE}" 0x14 0x00 < "${TMP}.blob" > "${TMP}.blob.ok"
"${FUTILITY}" gbb "${TMP}.blob.ok"
# And HWID of length 1 consisting only of '\0' is okay, too.
"${REPLACE}" 0x14 0x01 < "${TMP}.blob" | "${REPLACE}" 0x80 0x00 \
  > "${TMP}.blob.ok"
"${FUTILITY}" gbb "${TMP}.blob.ok"

# zero-length HWID not null-terminated is invalid
"${REPLACE}" 0x8f 0x41 < "${TMP}.blob" > "${TMP}.blob.bad"
if "${FUTILITY}" gbb "${TMP}.blob.bad"; then false; fi

#  hwid_offset < GBB_HEADER_SIZE is invalid
"${REPLACE}" 0x10 0x7f < "${TMP}.blob" > "${TMP}.blob.bad"
if "${FUTILITY}" gbb "${TMP}.blob.bad"; then false; fi
"${REPLACE}" 0x10 0x00 < "${TMP}.blob" > "${TMP}.blob.bad"
if "${FUTILITY}" gbb "${TMP}.blob.bad"; then false; fi

#  rootkey_offset < GBB_HEADER_SIZE is invalid
"${REPLACE}" 0x18 0x7f < "${TMP}.blob" > "${TMP}.blob.bad"
if "${FUTILITY}" gbb "${TMP}.blob.bad"; then false; fi
"${REPLACE}" 0x18 0x00 < "${TMP}.blob" > "${TMP}.blob.bad"
if "${FUTILITY}" gbb "${TMP}.blob.bad"; then false; fi

#  recovery_key_offset < GBB_HEADER_SIZE is invalid
"${REPLACE}" 0x28 0x7f < "${TMP}.blob" > "${TMP}.blob.bad"
if "${FUTILITY}" gbb "${TMP}.blob.bad"; then false; fi
"${REPLACE}" 0x28 0x00 < "${TMP}.blob" > "${TMP}.blob.bad"
if "${FUTILITY}" gbb "${TMP}.blob.bad"; then false; fi

#  hwid: offset + size  == end of file is okay; beyond is invalid
"${REPLACE}" 0x14 0x40 < "${TMP}.blob" > "${TMP}.blob.bad"
"${FUTILITY}" gbb -g "${TMP}.blob.bad"
"${REPLACE}" 0x14 0x41 < "${TMP}.blob" > "${TMP}.blob.bad"
if "${FUTILITY}" gbb -g "${TMP}.blob.bad"; then false; fi

#  rootkey: offset + size  == end of file is okay; beyond is invalid
"${REPLACE}" 0x1c 0x30 < "${TMP}.blob" > "${TMP}.blob.bad"
"${FUTILITY}" gbb -g "${TMP}.blob.bad"
"${REPLACE}" 0x1c 0x31 < "${TMP}.blob" > "${TMP}.blob.bad"
if "${FUTILITY}" gbb -g "${TMP}.blob.bad"; then false; fi

#  recovery_key: offset + size  == end of file is okay; beyond is invalid
"${REPLACE}" 0x2c 0x10 < "${TMP}.blob" > "${TMP}.blob.bad"
"${FUTILITY}" gbb -g "${TMP}.blob.bad"
"${REPLACE}" 0x2c 0x11 < "${TMP}.blob" > "${TMP}.blob.bad"
if "${FUTILITY}" gbb -g "${TMP}.blob.bad"; then false; fi

# hwid_size == 0 doesn't complain, but can't be set
"${REPLACE}" 0x14 0x00 < "${TMP}.blob" > "${TMP}.blob.bad"
"${FUTILITY}" gbb -g "${TMP}.blob.bad"
if "${FUTILITY}" gbb -s --hwid="A" "${TMP}.blob.bad"; then false; fi

# rootkey_size == 0 gives warning, gets nothing, can't be set
"${REPLACE}" 0x1c 0x00 < "${TMP}.blob" > "${TMP}.blob.bad"
"${FUTILITY}" gbb -g --rootkey     "${TMP}.read1" "${TMP}.blob.bad"
if "${FUTILITY}" gbb -s --rootkey  "${TMP}.data1" "${TMP}.blob.bad";
  then false; fi

# recovery_key_size == 0 gives warning, gets nothing, can't be set
"${REPLACE}" 0x2c 0x00 < "${TMP}.blob" > "${TMP}.blob.bad"
"${FUTILITY}" gbb -g --recoverykey "${TMP}.read2" "${TMP}.blob.bad"
if "${FUTILITY}" gbb -s --recoverykey "${TMP}.data2" "${TMP}.blob.bad";
  then false; fi


# GBB v1.2 adds a sha256 digest field in what was previously padding:
#
# hwid_digest:          0x0030  xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
#                       0x0040  xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
# pad:                  0x0050  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
#                       0x0060  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
#                       0x0070  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# (HWID)                0x0080  30 31 32 33 34 35 36 37 38 39 41 42 43 44 45 00

# See that the digest is updated properly.
hwid="123456789ABCDEF"
"${FUTILITY}" gbb -s --hwid="${hwid}" "${TMP}.blob"
expect=$(echo -n "$hwid" | sha256sum | cut -d ' ' -f 1)
[ "$(echo -n "${expect}" | wc -c)" == "64" ]
"${FUTILITY}" gbb -g --digest "${TMP}.blob" | grep "${expect}"

# Garble the digest, see that it's noticed.
# (assuming these zeros aren't present)
"${REPLACE}" 0x33 0x00 0x00 0x00 0x00 0x00 < "${TMP}.blob" > "${TMP}.blob.bad"
"${FUTILITY}" gbb -g --digest "${TMP}.blob.bad" | grep '0000000000'
"${FUTILITY}" gbb -g --digest "${TMP}.blob.bad" | grep 'invalid'

# Garble the HWID. The digest is unchanged, but now invalid.
"${REPLACE}" 0x84 0x70 0x71 0x72 < "${TMP}.blob" > "${TMP}.blob.bad"
"${FUTILITY}" gbb -g --digest "${TMP}.blob.bad" | grep 'invalid'

# cleanup
rm -f "${TMP}"*
exit 0
