#!/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 }