diff --git a/.gitignore b/.gitignore index 625fa1993ba055091a429ea6e42ddda06da25fc7..239f05a75a3154ed87d3f292a51d1309cb4fadc0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,3 @@ Cargo.lock # Prevent people from commiting sensitive files. crypto_data/ -src/ctap/key_material.rs - diff --git a/Cargo.toml b/Cargo.toml index 624c37ada605fecce84ac0a0fbf69583b8b3b321..2994acfe053943df6a0ff52d36f2b8e8901e3b2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,9 @@ panic_console = ["libtock/panic_console"] [dev-dependencies] elf2tab = "0.4.0" +[build-dependencies] +openssl = "0.10" + [profile.dev] panic = "abort" lto = true # Link Time Optimization usually reduces size of binaries and static libraries diff --git a/build.rs b/build.rs new file mode 100644 index 0000000000000000000000000000000000000000..053277eeac0645096fa24d0d16056e54e3aab808 --- /dev/null +++ b/build.rs @@ -0,0 +1,78 @@ +// 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. + +extern crate openssl; + +use openssl::asn1; +use openssl::ec; +use openssl::nid::Nid; +use openssl::pkey::PKey; +use openssl::x509; +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::Path; + +fn main() { + println!("cargo:rerun-if-changed=crypto_data/opensk.key"); + println!("cargo:rerun-if-changed=crypto_data/opensk_cert.pem"); + + let out_dir = env::var_os("OUT_DIR").unwrap(); + let priv_key_bin_path = Path::new(&out_dir).join("opensk_pkey.bin"); + let cert_bin_path = Path::new(&out_dir).join("opensk_cert.bin"); + let aaguid_bin_path = Path::new(&out_dir).join("opensk_aaguid.bin"); + + // Load the OpenSSL PEM ECC key + let ecc_data = include_bytes!("crypto_data/opensk.key"); + let pkey = ec::EcKey::private_key_from_pem(ecc_data) + .ok() + .expect("Failed to load OpenSK private key file"); + + // Check key validity + pkey.check_key().unwrap(); + assert_eq!(pkey.group().curve_name(), Some(Nid::X9_62_PRIME256V1)); + + let mut priv_key = pkey.private_key().to_vec(); + if priv_key.len() == 33 && priv_key[0] == 0 { + priv_key.remove(0); + } + assert_eq!(priv_key.len(), 32); + + // Create the raw private key out of the OpenSSL data + let mut priv_key_bin_file = File::create(&priv_key_bin_path).unwrap(); + priv_key_bin_file.write_all(&priv_key).unwrap(); + + // Convert the PEM certificate to DER and extract the serial for AAGUID + let input_pem_cert = include_bytes!("crypto_data/opensk_cert.pem"); + let cert = x509::X509::from_pem(input_pem_cert) + .ok() + .expect("Failed to load OpenSK certificate"); + + // Do some sanity check on the certificate + assert!(cert + .public_key() + .unwrap() + .public_eq(&PKey::from_ec_key(pkey).unwrap())); + let today = asn1::Asn1Time::days_from_now(0).unwrap(); + assert!(cert.not_after() > today); + assert!(cert.not_before() <= today); + + let mut cert_bin_file = File::create(&cert_bin_path).unwrap(); + cert_bin_file.write_all(&cert.to_der().unwrap()).unwrap(); + + let mut aaguid_bin_file = File::create(&aaguid_bin_path).unwrap(); + let mut serial = cert.serial_number().to_bn().unwrap().to_vec(); + serial.resize(16, 0); + aaguid_bin_file.write_all(&serial).unwrap(); +} diff --git a/src/ctap/key_material.rs b/src/ctap/key_material.rs new file mode 100644 index 0000000000000000000000000000000000000000..56f5252fe89b351bc9f1036b08a1f9efe57bc366 --- /dev/null +++ b/src/ctap/key_material.rs @@ -0,0 +1,21 @@ +// 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. + +pub const AAGUID: [u8; 16] = *include_bytes!(concat!(env!("OUT_DIR"), "/opensk_aaguid.bin")); + +pub const ATTESTATION_CERTIFICATE: &[u8] = + include_bytes!(concat!(env!("OUT_DIR"), "/opensk_cert.bin")); + +pub const ATTESTATION_PRIVATE_KEY: [u8; 32] = + *include_bytes!(concat!(env!("OUT_DIR"), "/opensk_pkey.bin")); diff --git a/tools/gen_key_materials.sh b/tools/gen_key_materials.sh index 260d8ebafebde381f5bebc5f3d30a72b55159e33..414626a3e5b297c645c53782c79a5feb9da649ae 100644 --- a/tools/gen_key_materials.sh +++ b/tools/gen_key_materials.sh @@ -36,6 +36,7 @@ generate_crypto_materials () { exit 1 fi + force_generate="$1" mkdir -p crypto_data if [ ! -f "${ca_priv_key}" ] then @@ -60,12 +61,12 @@ generate_crypto_materials () { -sha256 fi - if [ ! -f "${opensk_key}" ] + if [ "${force_generate}" = "Y" -o ! -f "${opensk_key}" ] then "${openssl}" ecparam -genkey -name prime256v1 -out "${opensk_key}" fi - if [ ! -f "${opensk_cert_name}.pem" ] + if [ "${force_generate}" = "Y" -o ! -f "${opensk_cert_name}.pem" ] then "${openssl}" req \ -new \ @@ -83,87 +84,4 @@ generate_crypto_materials () { -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 }