#!/bin/sh
#
# Copyright 2017-2020 Mellanox Technologies. All Rights Reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright notice,
#   this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of Mellanox nor the names of its contributors
#   may be used to endorse or promote products derived from this software
#   without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
#

set -e

KEY_DIR=$(dirname $(readlink -f $0))/keys

simple_keys_print_example()
{
  cat << EOF
  Generated test keys:
  PK key:
    ${KEY_DIR}/pk.key (private)
    ${KEY_DIR}/pk.cer (public in DER format)
    ${KEY_DIR}/pk.pem (public in PEM format)
  KEK key:
    ${KEY_DIR}/kek.key (private)
    ${KEY_DIR}/kek.cer (public in DER format)
    ${KEY_DIR}/kek.pem (public in PEM format)
  DB key:
    ${KEY_DIR}/db.key (private)
    ${KEY_DIR}/db.cer (public in DER format)
    ${KEY_DIR}/db.pem (public in PEM format)

  Below is an example command which could be used to create the capsule.
  The 'output.cap' is required. Others options can be provided as needed.

  mlx-mkcap \\
    --pk-key ${KEY_DIR}/pk.cer \\
    --kek-key ${KEY_DIR}/kek.cer \\
    --db-key ${KEY_DIR}/db.cer \\
    --signer-key ${KEY_DIR}/db.key \\
    --signer-cert ${KEY_DIR}/db.pem \\
    --uefi-passwd <plain_password> \\
    --embedded-driver <xxx.efi> \\
    --bootimg <default.bfb> \\
    <output.cap>
EOF
}

#
# Generate seperated EC test keys and X509 certificates. Also provide
# an example mlx-mkcap command.
#
simple_ec384_keys()
{
  echo "********************************************************"
  # Generate PK key
  openssl ecparam -name secp384r1 -genkey -noout -out ${KEY_DIR}/pk.key
  openssl req -new -x509 -sha384 -days 3650 -subj "/CN=Bluefield Platform Test Key/" \
    -key ${KEY_DIR}/pk.key -outform PEM -out ${KEY_DIR}/pk.pem
  openssl x509 -inform PEM -outform DER -in ${KEY_DIR}/pk.pem \
    -out ${KEY_DIR}/pk.cer

  # Generate KEK key
  openssl ecparam -name secp384r1 -genkey -noout -out ${KEY_DIR}/kek.key
  openssl req -new -x509 -sha384 -days 3650 -subj "/CN=Bluefield Key Exchange Test Key/" \
    -key ${KEY_DIR}/kek.key -outform DER -out ${KEY_DIR}/kek.cer

  # Generate DB key
  openssl ecparam -name secp384r1 -genkey -noout -out ${KEY_DIR}/db.key
  openssl req -new -x509 -sha384 -days 3650 -subj "/CN=Bluefield Signature Database Test Key/" \
    -key ${KEY_DIR}/db.key -outform PEM -out ${KEY_DIR}/db.pem
  openssl x509 -inform PEM -outform DER -in ${KEY_DIR}/db.pem \
    -out ${KEY_DIR}/db.cer
  cat ${KEY_DIR}/db.pem ${KEY_DIR}/db.key > ${KEY_DIR}/db.signer.pem
  echo "********************************************************"
  echo
  simple_keys_print_example
  echo "********************************************************"
}

#
# Generate seperated EC test keys and X509 certificates. Also provide
# an example mlx-mkcap command.
#
simple_rsa2048_keys()
{
  echo "********************************************************"
  # Generate PK key
  openssl req -newkey rsa:2048 -nodes -keyout ${KEY_DIR}/pk.key \
    -new -x509 -sha256 -days 3650 -subj "/CN=Bluefield Platform Test Key/" \
    -outform PEM -out ${KEY_DIR}/pk.pem
  openssl x509 -inform PEM -outform DER -in ${KEY_DIR}/pk.pem \
    -out ${KEY_DIR}/pk.cer

  # Generate KEK key
  openssl req -newkey rsa:2048 -nodes -keyout ${KEY_DIR}/kek.key \
    -new -x509 -sha256 -days 3650 -subj "/CN=Bluefield Key Exchange Test Key/" \
    -outform DER -out ${KEY_DIR}/kek.cer

  # Generate DB key
  openssl req -newkey rsa:2048 -nodes -keyout ${KEY_DIR}/db.key \
    -new -x509 -sha256 -days 3650 -subj "/CN=Bluefield Signature Database Test Key/" \
    -outform PEM -out ${KEY_DIR}/db.pem
  openssl x509 -inform PEM -outform DER -in ${KEY_DIR}/db.pem -out ${KEY_DIR}/db.cer
  cat ${KEY_DIR}/db.pem ${KEY_DIR}/db.key > ${KEY_DIR}/db.signer.pem
  echo "********************************************************"
  echo
  simple_keys_print_example
  echo "********************************************************"
}

#
# Follow edk2/BaseTools/Source/Python/Pkcs7Sign/Readme.md to
# create the certificate chain.
#
chained_keys()
{
  rm -rf ${KEY_DIR}/newcerts 2>/dev/null
  mkdir -p ${KEY_DIR}/newcerts 2>/dev/null
  touch ${KEY_DIR}/index.txt
  if [ ! -e ${KEY_DIR}/serial ]; then
    echo 01 > ${KEY_DIR}/serial
  fi

  echo "********************************************************"
  echo " Generate the root key"
  echo "********************************************************"
  openssl genrsa -out ${KEY_DIR}/TestRoot.key 2048
  echo

  echo "********************************************************"
  echo " Generate the self-signed root certificate"
  echo "********************************************************"
  openssl req -extensions v3_ca -new -x509 -days 3650 \
    -key ${KEY_DIR}/TestRoot.key -out ${KEY_DIR}/TestRoot.crt \
    -config openssl.cnf
  openssl x509 -in ${KEY_DIR}/TestRoot.crt -outform DER \
    -out ${KEY_DIR}/TestRoot.cer
  openssl x509 -inform DER -in ${KEY_DIR}/TestRoot.cer -outform PEM \
    -out ${KEY_DIR}/TestRoot.pem
  echo

  echo "********************************************************"
  echo " Generate the intermediate key"
  echo "********************************************************"
  openssl genrsa -out ${KEY_DIR}/TestSub.key 2048
  echo

  echo "********************************************************"
  echo " Generate the intermediate certificate"
  echo "********************************************************"
  openssl req -new -days 3650 -key ${KEY_DIR}/TestSub.key \
    -out ${KEY_DIR}/TestSub.csr -config openssl.cnf
  openssl ca -extensions v3_intermediate_ca -in ${KEY_DIR}/TestSub.csr \
    -days 3650 -out ${KEY_DIR}/TestSub.crt -cert ${KEY_DIR}/TestRoot.crt \
    -keyfile ${KEY_DIR}/TestRoot.key -config openssl.cnf
  openssl x509 -in ${KEY_DIR}/TestSub.crt -outform DER \
    -out ${KEY_DIR}/TestSub.cer
  openssl x509 -inform DER -in ${KEY_DIR}/TestSub.cer \
    -outform PEM -out ${KEY_DIR}/TestSub.pem
  echo

  echo "********************************************************"
  echo " Generate the User key"
  echo "********************************************************"
  openssl genrsa -out ${KEY_DIR}/TestUser.key 2048

  echo "********************************************************"
  echo " Generate the User certificate"
  echo "********************************************************"
  openssl req -new -days 3650 -key ${KEY_DIR}/TestUser.key \
    -out ${KEY_DIR}/TestUser.csr -config openssl.cnf
  openssl ca -extensions usr_cert -in ${KEY_DIR}/TestUser.csr \
    -days 3650 -out ${KEY_DIR}/TestUser.crt -cert ${KEY_DIR}/TestSub.crt \
    -keyfile ${KEY_DIR}/TestSub.key -config openssl.cnf
  openssl x509 -in ${KEY_DIR}/TestUser.crt -outform DER \
    -out ${KEY_DIR}/TestUser.cer
  openssl x509 -inform DER -in ${KEY_DIR}/TestUser.cer \
    -outform PEM -out ${KEY_DIR}/TestUser.pem
  echo "********************************************************"
  echo

  cat << EOF
  Generated test keys:
  Root key:
    ${KEY_DIR}/TestRoot.key     (private)
    ${KEY_DIR}/TestRoot.cer     (public in DER format)
    ${KEY_DIR}/TestRoot.pem     (public in PEM format)
  Intermediate key:
    ${KEY_DIR}/TestSub.key      (private)
    ${KEY_DIR}/TestSub.cer      (public in DER format)
    ${KEY_DIR}/TestSub.pem      (public in PEM format)
  User key:
    ${KEY_DIR}/TestUser.key     (private)
    ${KEY_DIR}/TestUser.cer     (public in DER format)
    ${KEY_DIR}/TestUser.pem     (public in PEM format)

  Below is an example command which could be used to create the capsule.
  The 'output.cap' is required. Others options can be provided as needed.

  mlx-mkcap \\
    --pk-key ${KEY_DIR}/TestRoot.cer \\
    --kek-key ${KEY_DIR}/TestSub.cer \\
    --db-key ${KEY_DIR}/TestUser.cer \\
    --signer-key ${KEY_DIR}/TestUser.key \\
    --signer-cert ${KEY_DIR}/TestUser.pem \\
    --other-public-cert ${KEY_DIR}/TestRoot.pem \\
    --uefi-passwd <plain_password> \\
    --embedded-driver <xxx.efi> \\
    --bootimg <default.bfb> \\
    <output.cap>
EOF
}

usage()
{
  echo "syntax: mlx-mkkeys [-h|--help] [--simple-rsa-keys] [--simple-ec-keys] [--chained-keys]"
}

mkdir -p ${KEY_DIR} 2>/dev/null

options=`getopt -n mlx-mkkeys -o h \
         -l help,simple-rsa-keys,simple-ec-keys,chained-keys -- "$@"`
eval set -- $options
while [ "$1" != -- ]; do
  case $1 in
    --help|-h) usage; exit 0 ;;
    --simple-rsa-keys) simple_rsa2048_keys; exit 0 ;;
    --simple-ec-keys) simple_ec384_keys; exit 0 ;;
    --chained-keys) chained_keys; exit 0 ;;
  esac
  shift
done

usage >&2
exit 1
