From 596b47886c9ed9beae6db22e7d7383a291d623a6 Mon Sep 17 00:00:00 2001 From: kaczmarczyck <43844792+kaczmarczyck@users.noreply.github.com> Date: Wed, 15 Sep 2021 21:25:19 +0200 Subject: [PATCH] Upgrade signing key generation (#379) * adds the upgrade signing key generation and the partition offset * use openssl in build.rs instead --- Cargo.toml | 2 ++ build.rs | 43 ++++++++++++++++++++++++++- docs/install.md | 22 +++++++------- src/ctap/data_formats.rs | 21 +------------ src/ctap/key_material.rs | 3 ++ src/embedded_flash/buffer_upgrade.rs | 4 +++ src/embedded_flash/syscall.rs | 4 +++ src/embedded_flash/upgrade_storage.rs | 3 ++ tools/gen_key_materials.sh | 16 ++++++++++ 9 files changed, 87 insertions(+), 31 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 44e6feb..f2f72e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,9 @@ with_nfc = ["libtock_drivers/with_nfc"] enum-iterator = "0.6.0" [build-dependencies] +sk-cbor = { path = "libraries/cbor" } uuid = { version = "0.8", features = ["v4"] } +openssl = "0.10.36" [profile.dev] panic = "abort" diff --git a/build.rs b/build.rs index 2a31606..184cd3b 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// Copyright 2019-2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,7 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +extern crate alloc; + +use openssl::bn; +use openssl::ec; +use openssl::nid; +use sk_cbor::cbor_map; use std::env; +use std::fs; use std::fs::File; use std::io::Read; use std::io::Write; @@ -20,7 +27,9 @@ use std::path::Path; use uuid::Uuid; fn main() { + const UPGRADE_FILE: &str = "crypto_data/opensk_upgrade_pub.pem"; println!("cargo:rerun-if-changed=crypto_data/aaguid.txt"); + println!("cargo:rerun-if-changed={}", UPGRADE_FILE); println!("cargo:rerun-if-changed=layout.ld"); println!("cargo:rerun-if-changed=nrf52840_layout.ld"); @@ -34,4 +43,36 @@ fn main() { content.truncate(36); let aaguid = Uuid::parse_str(&content).unwrap(); aaguid_bin_file.write_all(aaguid.as_bytes()).unwrap(); + + // COSE encoding the public key, then write it out. + let pem_bytes = fs::read(UPGRADE_FILE).unwrap(); + let ec_key = ec::EcKey::public_key_from_pem(&pem_bytes).ok().unwrap(); + let group = ec::EcGroup::from_curve_name(nid::Nid::X9_62_PRIME256V1).unwrap(); + let conversion_form = ec::PointConversionForm::UNCOMPRESSED; + let mut ctx = bn::BigNumContext::new().unwrap(); + let raw_bytes = ec_key + .public_key() + .to_bytes(&group, conversion_form, &mut ctx) + .unwrap(); + const POINT_LEN: usize = 32; + assert_eq!(raw_bytes.len(), 1 + 2 * POINT_LEN); + assert_eq!(raw_bytes[0], 0x04); + let x_bytes = &raw_bytes[1..][..POINT_LEN]; + let y_bytes = &raw_bytes[1 + POINT_LEN..][..POINT_LEN]; + + const EC2_KEY_TYPE: i64 = 2; + const P_256_CURVE: i64 = 1; + const ES256_ALGORITHM: i64 = -7; + let pub_key_cbor = sk_cbor::cbor_map! { + 1 => EC2_KEY_TYPE, + 3 => ES256_ALGORITHM, + -1 => P_256_CURVE, + -2 => x_bytes, + -3 => y_bytes, + }; + let mut cbor_bytes = vec![]; + sk_cbor::writer::write(pub_key_cbor, &mut cbor_bytes).unwrap(); + let upgrade_pubkey_path = Path::new(&out_dir).join("opensk_upgrade_pubkey_cbor.bin"); + let mut upgrade_pub_bin_file = File::create(&upgrade_pubkey_path).unwrap(); + upgrade_pub_bin_file.write_all(&cbor_bytes).unwrap(); } diff --git a/docs/install.md b/docs/install.md index d5ee73f..a3b8dd1 100644 --- a/docs/install.md +++ b/docs/install.md @@ -124,16 +124,18 @@ All the generated certificates and private keys are stored in the directory This is the expected content after running our `setup.sh` script: -File | Purpose ------------------ | -------------------------------------------------------- -`aaguid.txt` | Text file containaing the AAGUID value -`opensk_ca.csr` | Certificate sign request for the Root CA -`opensk_ca.key` | ECC secp256r1 private key used for the Root CA -`opensk_ca.pem` | PEM encoded certificate of the Root CA -`opensk_ca.srl` | File generated by OpenSSL -`opensk_cert.csr` | Certificate sign request for the attestation certificate -`opensk_cert.pem` | PEM encoded certificate used for the authenticator -`opensk.key` | ECC secp256r1 private key used for the autenticator +File | Purpose +------------------------ | -------------------------------------------------------- +`aaguid.txt` | Text file containaing the AAGUID value +`opensk_ca.csr` | Certificate sign request for the Root CA +`opensk_ca.key` | ECC secp256r1 private key used for the Root CA +`opensk_ca.pem` | PEM encoded certificate of the Root CA +`opensk_ca.srl` | File generated by OpenSSL +`opensk_cert.csr` | Certificate sign request for the attestation certificate +`opensk_cert.pem` | PEM encoded certificate used for the authenticator +`opensk.key` | ECC secp256r1 private key used for the autenticator +`opensk_upgrade.key` | Private key for signing upgrades through CTAP +`opensk_upgrade_pub.pem` | Public key added to the firmware for verifying upgrades If you want to use your own attestation certificate and private key, simply replace `opensk_cert.pem` and `opensk.key` files. diff --git a/src/ctap/data_formats.rs b/src/ctap/data_formats.rs index f8b875a..b91b1cc 100644 --- a/src/ctap/data_formats.rs +++ b/src/ctap/data_formats.rs @@ -17,7 +17,6 @@ use alloc::string::String; use alloc::vec::Vec; use arrayref::array_ref; use core::convert::TryFrom; -use core::fmt; use crypto::{ecdh, ecdsa}; #[cfg(test)] use enum_iterator::IntoEnumIterator; @@ -841,30 +840,12 @@ impl TryFrom for ecdsa::PubKey { /// Data structure for receiving a signature. /// /// See https://datatracker.ietf.org/doc/html/rfc8152#appendix-C.1.1 for reference. -/// -/// TODO derive Debug and PartialEq with compiler version 1.47 -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq)] pub struct CoseSignature { pub algorithm: SignatureAlgorithm, pub bytes: [u8; ecdsa::Signature::BYTES_LENGTH], } -impl fmt::Debug for CoseSignature { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter - .debug_struct("CoseSignature") - .field("algorithm", &self.algorithm) - .field("bytes", &self.bytes.to_vec()) - .finish() - } -} - -impl PartialEq for CoseSignature { - fn eq(&self, other: &CoseSignature) -> bool { - self.algorithm == other.algorithm && self.bytes[..] == other.bytes[..] - } -} - impl TryFrom for CoseSignature { type Error = Ctap2StatusCode; diff --git a/src/ctap/key_material.rs b/src/ctap/key_material.rs index a8ae6da..3db0c07 100644 --- a/src/ctap/key_material.rs +++ b/src/ctap/key_material.rs @@ -14,6 +14,9 @@ pub const ATTESTATION_PRIVATE_KEY_LENGTH: usize = 32; pub const AAGUID_LENGTH: usize = 16; +pub const _UPGRADE_PUBLIC_KEY_LENGTH: usize = 77; pub const AAGUID: &[u8; AAGUID_LENGTH] = include_bytes!(concat!(env!("OUT_DIR"), "/opensk_aaguid.bin")); +pub const _UPGRADE_PUBLIC_KEY: &[u8; _UPGRADE_PUBLIC_KEY_LENGTH] = + include_bytes!(concat!(env!("OUT_DIR"), "/opensk_upgrade_pubkey_cbor.bin")); diff --git a/src/embedded_flash/buffer_upgrade.rs b/src/embedded_flash/buffer_upgrade.rs index d01e913..780d3c3 100644 --- a/src/embedded_flash/buffer_upgrade.rs +++ b/src/embedded_flash/buffer_upgrade.rs @@ -57,6 +57,10 @@ impl UpgradeStorage for BufferUpgradeStorage { } } + fn partition_address(&self) -> usize { + 0x60000 + } + fn partition_length(&self) -> usize { PARTITION_LENGTH } diff --git a/src/embedded_flash/syscall.rs b/src/embedded_flash/syscall.rs index bec385c..0bd5243 100644 --- a/src/embedded_flash/syscall.rs +++ b/src/embedded_flash/syscall.rs @@ -317,6 +317,10 @@ impl UpgradeStorage for SyscallUpgradeStorage { } } + fn partition_address(&self) -> usize { + self.partition.start() + } + fn partition_length(&self) -> usize { self.partition.length() } diff --git a/src/embedded_flash/upgrade_storage.rs b/src/embedded_flash/upgrade_storage.rs index 1199a35..44e8583 100644 --- a/src/embedded_flash/upgrade_storage.rs +++ b/src/embedded_flash/upgrade_storage.rs @@ -34,6 +34,9 @@ pub trait UpgradeStorage { /// Returns [`StorageError::OutOfBounds`] if the data does not fit the partition. fn write_partition(&mut self, offset: usize, data: &[u8]) -> StorageResult<()>; + /// Returns the address of the partition. + fn partition_address(&self) -> usize; + /// Returns the length of the partition. fn partition_length(&self) -> usize; diff --git a/tools/gen_key_materials.sh b/tools/gen_key_materials.sh index d9aa432..ecdfd96 100755 --- a/tools/gen_key_materials.sh +++ b/tools/gen_key_materials.sh @@ -26,6 +26,11 @@ generate_crypto_materials () { local opensk_key=crypto_data/opensk.key local opensk_cert_name=crypto_data/opensk_cert + # The upgrade private key is used for signing, the corresponding public key + # will be COSE encoded and embedded into the firmware. + local opensk_upgrade=crypto_data/opensk_upgrade.key + local opensk_upgrade_pub=crypto_data/opensk_upgrade_pub.pem + # Allow invoker to override the command with a full path. local openssl=${OPENSSL:-$(which openssl)} @@ -88,6 +93,17 @@ generate_crypto_materials () { -sha256 fi + if [ "${force_generate}" = "Y" -o ! -f "${opensk_upgrade}" ] + then + "${openssl}" ecparam -genkey -name prime256v1 -out "${opensk_upgrade}" + rm -f "${opensk_upgrade_pub}" + fi + + if [ "${force_generate}" = "Y" -o ! -f "${opensk_upgrade_pub}" ] + then + "${openssl}" ec -in "${opensk_upgrade}" -pubout -out "${opensk_upgrade_pub}" + fi + if [ "${force_generate}" = "Y" -o ! -f "${aaguid_file}" ] then uuidgen > "${aaguid_file}"