170 lines
5.5 KiB
Bash
170 lines
5.5 KiB
Bash
#!/bin/bash
|
|
# Copyright 2019 Google LLC
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
generate_crypto_materials () {
|
|
# Root CA key pair and certificate
|
|
local ca_priv_key=crypto_data/opensk_ca.key
|
|
local ca_cert_name=crypto_data/opensk_ca
|
|
|
|
# Attestation key pair and certificate that will be embedded into the
|
|
# firmware. The certificate will be signed by the Root CA.
|
|
local opensk_key=crypto_data/opensk.key
|
|
local opensk_cert_name=crypto_data/opensk_cert
|
|
|
|
# Rust file that we will generate will all cryptographic data.
|
|
local rust_file=src/ctap/key_material.rs
|
|
|
|
# Allow invoker to override the command with a full path.
|
|
local openssl=${OPENSSL:-$(which openssl)}
|
|
|
|
# We need openssl command to continue
|
|
if [ ! -x "${openssl}" ]
|
|
then
|
|
echo "Missing openssl command. Try to specify its full path using OPENSSL environment variable."
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p crypto_data
|
|
if [ ! -f "${ca_priv_key}" ]
|
|
then
|
|
"${openssl}" ecparam -genkey -name prime256v1 -out "${ca_priv_key}"
|
|
fi
|
|
|
|
if [ ! -f "${ca_cert_name}.pem" ]
|
|
then
|
|
"${openssl}" req \
|
|
-new \
|
|
-key "${ca_priv_key}" \
|
|
-out "${ca_cert_name}.csr" \
|
|
-subj "/CN=Google OpenSK CA"
|
|
"${openssl}" x509 \
|
|
-trustout \
|
|
-req \
|
|
-days 7305 \
|
|
-in "${ca_cert_name}.csr" \
|
|
-signkey "${ca_priv_key}" \
|
|
-outform pem \
|
|
-out "${ca_cert_name}.pem" \
|
|
-sha256
|
|
fi
|
|
|
|
if [ ! -f "${opensk_key}" ]
|
|
then
|
|
"${openssl}" ecparam -genkey -name prime256v1 -out "${opensk_key}"
|
|
fi
|
|
|
|
if [ ! -f "${opensk_cert_name}.pem" ]
|
|
then
|
|
"${openssl}" req \
|
|
-new \
|
|
-key "${opensk_key}" \
|
|
-out "${opensk_cert_name}.csr" \
|
|
-subj "/CN=Google OpenSK Hacker Edition"
|
|
"${openssl}" x509 \
|
|
-req \
|
|
-days 3652 \
|
|
-in "${opensk_cert_name}.csr" \
|
|
-CA "${ca_cert_name}.pem" \
|
|
-CAkey "${ca_priv_key}" \
|
|
-CAcreateserial \
|
|
-outform pem \
|
|
-out "${opensk_cert_name}.pem" \
|
|
-sha256
|
|
fi
|
|
|
|
local cert_mtime=$(stat --printf="%Y" "${opensk_cert_name}.pem")
|
|
local rust_file_mtime=0
|
|
# Only take into consideration the mtime of the file if it exists and if we're not forcing
|
|
# the rust file to be re-generated.
|
|
if [ -f "${rust_file}" -a "x$1" != "xY" ]
|
|
then
|
|
rust_file_mtime=$(stat --printf="%Y" "${rust_file}")
|
|
fi
|
|
if [ $cert_mtime -gt $rust_file_mtime ]
|
|
then
|
|
local cert_size=$("${openssl}" x509 \
|
|
-in "${opensk_cert_name}.pem" \
|
|
-outform der 2>/dev/null \
|
|
| wc -c)
|
|
local cert_serial_hex=$("${openssl}" x509 \
|
|
-in "${opensk_cert_name}.pem" \
|
|
-noout \
|
|
-serial \
|
|
| cut -d'=' -f2)
|
|
# Pad with zeroes in case the serial is too short. We don't care if the
|
|
# serial is longer than 32 characters as we will only process the first 32
|
|
# characters in the loop later.
|
|
cert_serial_hex="${cert_serial_hex}00000000000000000000000000000000"
|
|
|
|
# Create header
|
|
echo "// This file had been generated by OpenSK deploy.sh script" > "${rust_file}"
|
|
echo "" >> "${rust_file}"
|
|
|
|
echo "pub const AAGUID: [u8; 16] = [" >> "${rust_file}"
|
|
for i in `seq 0 2 30`
|
|
do
|
|
echo -n "0x${cert_serial_hex:$i:2}, " >> "${rust_file}"
|
|
done
|
|
echo "" >> "${rust_file}"
|
|
echo "];" >> "${rust_file}"
|
|
echo "" >> "${rust_file}"
|
|
|
|
echo "pub const ATTESTATION_CERTIFICATE: [u8; ${cert_size}] = [" >> "${rust_file}"
|
|
"${openssl}" x509 \
|
|
-in "${opensk_cert_name}.pem" \
|
|
-outform der 2>/dev/null \
|
|
| xxd -i >> "${rust_file}"
|
|
echo "];" >> "${rust_file}"
|
|
echo "" >> "${rust_file}"
|
|
|
|
# Private key is tricky to extract as we want the raw value and not the DER encoding
|
|
# Example output of openssl ec -in file.key -noout -text:
|
|
# read EC key
|
|
# Private-Key: (256 bit)
|
|
# priv:
|
|
# 47:b3:58:b8:f0:09:1d:72:b1:03:34:62:9a:c7:b2:
|
|
# b2:e1:06:28:15:69:d4:82:b5:4e:21:6d:98:bf:65:
|
|
# 98:34
|
|
# pub:
|
|
# 04:32:84:a1:3c:90:db:3f:db:d7:fb:ff:e9:00:c8:
|
|
# 8a:a1:79:2e:95:2e:7c:86:ec:19:03:97:6e:7c:d6:
|
|
# 67:eb:28:56:f1:d8:dd:cb:ae:ce:b9:cb:e4:6d:9d:
|
|
# 1d:76:96:fc:48:9b:2d:d5:80:86:04:3d:f9:fe:6c:
|
|
# f3:9a:45:bc:b1
|
|
# ASN1 OID: prime256v1
|
|
# NIST CURVE: P-256
|
|
#
|
|
# The awk script starts printing lines after seeing a line starting with
|
|
# "priv:" and stops printing as soon as it reaches a line that doesn't start
|
|
# with a space.
|
|
# The sed script then converts the output into a proper hex-encode byte
|
|
# array by replacing the initial spaces on each line with "0x", replacing
|
|
# the semicolons at the end of each line by commas and replacing all
|
|
# remainging semicolons by ". 0x".
|
|
echo "pub const ATTESTATION_PRIVATE_KEY: [u8; 32] = [" >> "${rust_file}"
|
|
"${openssl}" ec \
|
|
-in "${opensk_key}" \
|
|
-noout \
|
|
-text 2>/dev/null \
|
|
| awk '/^priv:/{p=1;next}/^[^ ]/{p=0}p' \
|
|
| sed -e 's/^ */0x/;s/:$/,/;s/:/, 0x/g' >> "${rust_file}"
|
|
echo "];" >> "${rust_file}"
|
|
echo "" >> "${rust_file}"
|
|
|
|
# If the tool is installed, prettify the file. It will catch syntax errors earlier.
|
|
which rustfmt > /dev/null 2>&1 && rustfmt "${rust_file}"
|
|
fi
|
|
}
|