Upgrade signing key generation (#379)
* adds the upgrade signing key generation and the partition offset * use openssl in build.rs instead
This commit is contained in:
@@ -33,7 +33,9 @@ with_nfc = ["libtock_drivers/with_nfc"]
|
|||||||
enum-iterator = "0.6.0"
|
enum-iterator = "0.6.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
sk-cbor = { path = "libraries/cbor" }
|
||||||
uuid = { version = "0.8", features = ["v4"] }
|
uuid = { version = "0.8", features = ["v4"] }
|
||||||
|
openssl = "0.10.36"
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|||||||
43
build.rs
43
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");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with 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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// 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::env;
|
||||||
|
use std::fs;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
@@ -20,7 +27,9 @@ use std::path::Path;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
fn main() {
|
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=crypto_data/aaguid.txt");
|
||||||
|
println!("cargo:rerun-if-changed={}", UPGRADE_FILE);
|
||||||
println!("cargo:rerun-if-changed=layout.ld");
|
println!("cargo:rerun-if-changed=layout.ld");
|
||||||
println!("cargo:rerun-if-changed=nrf52840_layout.ld");
|
println!("cargo:rerun-if-changed=nrf52840_layout.ld");
|
||||||
|
|
||||||
@@ -34,4 +43,36 @@ fn main() {
|
|||||||
content.truncate(36);
|
content.truncate(36);
|
||||||
let aaguid = Uuid::parse_str(&content).unwrap();
|
let aaguid = Uuid::parse_str(&content).unwrap();
|
||||||
aaguid_bin_file.write_all(aaguid.as_bytes()).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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ All the generated certificates and private keys are stored in the directory
|
|||||||
This is the expected content after running our `setup.sh` script:
|
This is the expected content after running our `setup.sh` script:
|
||||||
|
|
||||||
File | Purpose
|
File | Purpose
|
||||||
----------------- | --------------------------------------------------------
|
------------------------ | --------------------------------------------------------
|
||||||
`aaguid.txt` | Text file containaing the AAGUID value
|
`aaguid.txt` | Text file containaing the AAGUID value
|
||||||
`opensk_ca.csr` | Certificate sign request for the Root CA
|
`opensk_ca.csr` | Certificate sign request for the Root CA
|
||||||
`opensk_ca.key` | ECC secp256r1 private key used for the Root CA
|
`opensk_ca.key` | ECC secp256r1 private key used for the Root CA
|
||||||
@@ -134,6 +134,8 @@ File | Purpose
|
|||||||
`opensk_cert.csr` | Certificate sign request for the attestation certificate
|
`opensk_cert.csr` | Certificate sign request for the attestation certificate
|
||||||
`opensk_cert.pem` | PEM encoded certificate used for the authenticator
|
`opensk_cert.pem` | PEM encoded certificate used for the authenticator
|
||||||
`opensk.key` | ECC secp256r1 private key used for the autenticator
|
`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
|
If you want to use your own attestation certificate and private key, simply
|
||||||
replace `opensk_cert.pem` and `opensk.key` files.
|
replace `opensk_cert.pem` and `opensk.key` files.
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ use alloc::string::String;
|
|||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use arrayref::array_ref;
|
use arrayref::array_ref;
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use core::fmt;
|
|
||||||
use crypto::{ecdh, ecdsa};
|
use crypto::{ecdh, ecdsa};
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use enum_iterator::IntoEnumIterator;
|
use enum_iterator::IntoEnumIterator;
|
||||||
@@ -841,30 +840,12 @@ impl TryFrom<CoseKey> for ecdsa::PubKey {
|
|||||||
/// Data structure for receiving a signature.
|
/// Data structure for receiving a signature.
|
||||||
///
|
///
|
||||||
/// See https://datatracker.ietf.org/doc/html/rfc8152#appendix-C.1.1 for reference.
|
/// See https://datatracker.ietf.org/doc/html/rfc8152#appendix-C.1.1 for reference.
|
||||||
///
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
/// TODO derive Debug and PartialEq with compiler version 1.47
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct CoseSignature {
|
pub struct CoseSignature {
|
||||||
pub algorithm: SignatureAlgorithm,
|
pub algorithm: SignatureAlgorithm,
|
||||||
pub bytes: [u8; ecdsa::Signature::BYTES_LENGTH],
|
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<cbor::Value> for CoseSignature {
|
impl TryFrom<cbor::Value> for CoseSignature {
|
||||||
type Error = Ctap2StatusCode;
|
type Error = Ctap2StatusCode;
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,9 @@
|
|||||||
|
|
||||||
pub const ATTESTATION_PRIVATE_KEY_LENGTH: usize = 32;
|
pub const ATTESTATION_PRIVATE_KEY_LENGTH: usize = 32;
|
||||||
pub const AAGUID_LENGTH: usize = 16;
|
pub const AAGUID_LENGTH: usize = 16;
|
||||||
|
pub const _UPGRADE_PUBLIC_KEY_LENGTH: usize = 77;
|
||||||
|
|
||||||
pub const AAGUID: &[u8; AAGUID_LENGTH] =
|
pub const AAGUID: &[u8; AAGUID_LENGTH] =
|
||||||
include_bytes!(concat!(env!("OUT_DIR"), "/opensk_aaguid.bin"));
|
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"));
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ impl UpgradeStorage for BufferUpgradeStorage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn partition_address(&self) -> usize {
|
||||||
|
0x60000
|
||||||
|
}
|
||||||
|
|
||||||
fn partition_length(&self) -> usize {
|
fn partition_length(&self) -> usize {
|
||||||
PARTITION_LENGTH
|
PARTITION_LENGTH
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -317,6 +317,10 @@ impl UpgradeStorage for SyscallUpgradeStorage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn partition_address(&self) -> usize {
|
||||||
|
self.partition.start()
|
||||||
|
}
|
||||||
|
|
||||||
fn partition_length(&self) -> usize {
|
fn partition_length(&self) -> usize {
|
||||||
self.partition.length()
|
self.partition.length()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ pub trait UpgradeStorage {
|
|||||||
/// Returns [`StorageError::OutOfBounds`] if the data does not fit the partition.
|
/// Returns [`StorageError::OutOfBounds`] if the data does not fit the partition.
|
||||||
fn write_partition(&mut self, offset: usize, data: &[u8]) -> StorageResult<()>;
|
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.
|
/// Returns the length of the partition.
|
||||||
fn partition_length(&self) -> usize;
|
fn partition_length(&self) -> usize;
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ generate_crypto_materials () {
|
|||||||
local opensk_key=crypto_data/opensk.key
|
local opensk_key=crypto_data/opensk.key
|
||||||
local opensk_cert_name=crypto_data/opensk_cert
|
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.
|
# Allow invoker to override the command with a full path.
|
||||||
local openssl=${OPENSSL:-$(which openssl)}
|
local openssl=${OPENSSL:-$(which openssl)}
|
||||||
|
|
||||||
@@ -88,6 +93,17 @@ generate_crypto_materials () {
|
|||||||
-sha256
|
-sha256
|
||||||
fi
|
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}" ]
|
if [ "${force_generate}" = "Y" -o ! -f "${aaguid_file}" ]
|
||||||
then
|
then
|
||||||
uuidgen > "${aaguid_file}"
|
uuidgen > "${aaguid_file}"
|
||||||
|
|||||||
Reference in New Issue
Block a user