Firmware version for upgrades (#542)
* shows and checks the firmware version * merges metadata ranges in boards * simplifies locations loop
This commit is contained in:
@@ -25,8 +25,8 @@ static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 5] = [
|
|||||||
},
|
},
|
||||||
// Partitions can also be split to maximize MPU happiness.
|
// Partitions can also be split to maximize MPU happiness.
|
||||||
kernel::StorageLocation {
|
kernel::StorageLocation {
|
||||||
address: 0x5000,
|
address: 0x4000,
|
||||||
size: 0x1000,
|
size: 0x2000,
|
||||||
storage_type: kernel::StorageType::Partition,
|
storage_type: kernel::StorageType::Partition,
|
||||||
},
|
},
|
||||||
kernel::StorageLocation {
|
kernel::StorageLocation {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 5] = [
|
|||||||
// Partitions can also be split to maximize MPU happiness.
|
// Partitions can also be split to maximize MPU happiness.
|
||||||
kernel::StorageLocation {
|
kernel::StorageLocation {
|
||||||
address: 0x4000,
|
address: 0x4000,
|
||||||
size: 0x1000,
|
size: 0x2000,
|
||||||
storage_type: kernel::StorageType::Partition,
|
storage_type: kernel::StorageType::Partition,
|
||||||
},
|
},
|
||||||
kernel::StorageLocation {
|
kernel::StorageLocation {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 Google LLC
|
// Copyright 2021-2022 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.
|
||||||
@@ -53,4 +53,7 @@ pub trait UpgradeStorage {
|
|||||||
///
|
///
|
||||||
/// Returns [`StorageError::OutOfBounds`] if the data is too long to fit the metadata storage.
|
/// Returns [`StorageError::OutOfBounds`] if the data is too long to fit the metadata storage.
|
||||||
fn write_metadata(&mut self, data: &[u8]) -> StorageResult<()>;
|
fn write_metadata(&mut self, data: &[u8]) -> StorageResult<()>;
|
||||||
|
|
||||||
|
/// Returns the currently running firmware version.
|
||||||
|
fn running_firmware_version(&self) -> u64;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1295,7 +1295,7 @@ impl CtapState {
|
|||||||
),
|
),
|
||||||
force_pin_change: Some(storage::has_force_pin_change(env)?),
|
force_pin_change: Some(storage::has_force_pin_change(env)?),
|
||||||
min_pin_length: storage::min_pin_length(env)?,
|
min_pin_length: storage::min_pin_length(env)?,
|
||||||
firmware_version: None,
|
firmware_version: env.upgrade_storage().map(|u| u.running_firmware_version()),
|
||||||
max_cred_blob_length: Some(env.customization().max_cred_blob_length() as u64),
|
max_cred_blob_length: Some(env.customization().max_cred_blob_length() as u64),
|
||||||
max_rp_ids_for_set_min_pin_length: Some(
|
max_rp_ids_for_set_min_pin_length: Some(
|
||||||
env.customization().max_rp_ids_length() as u64
|
env.customization().max_rp_ids_length() as u64
|
||||||
@@ -1590,6 +1590,7 @@ mod test {
|
|||||||
0x0B => env.customization().max_large_blob_array_size() as u64,
|
0x0B => env.customization().max_large_blob_array_size() as u64,
|
||||||
0x0C => false,
|
0x0C => false,
|
||||||
0x0D => storage::min_pin_length(&mut env).unwrap() as u64,
|
0x0D => storage::min_pin_length(&mut env).unwrap() as u64,
|
||||||
|
0x0E => 0,
|
||||||
0x0F => env.customization().max_cred_blob_length() as u64,
|
0x0F => env.customization().max_cred_blob_length() as u64,
|
||||||
0x10 => env.customization().max_rp_ids_length() as u64,
|
0x10 => env.customization().max_rp_ids_length() as u64,
|
||||||
0x14 => storage::remaining_credentials(&mut env).unwrap() as u64,
|
0x14 => storage::remaining_credentials(&mut env).unwrap() as u64,
|
||||||
|
|||||||
4
src/env/test/upgrade_storage.rs
vendored
4
src/env/test/upgrade_storage.rs
vendored
@@ -84,6 +84,10 @@ impl UpgradeStorage for BufferUpgradeStorage {
|
|||||||
Err(StorageError::OutOfBounds)
|
Err(StorageError::OutOfBounds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn running_firmware_version(&self) -> u64 {
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
58
src/env/tock/storage.rs
vendored
58
src/env/tock/storage.rs
vendored
@@ -25,6 +25,7 @@ use libtock_core::{callback, syscalls};
|
|||||||
use persistent_store::{Storage, StorageError, StorageIndex, StorageResult};
|
use persistent_store::{Storage, StorageError, StorageIndex, StorageResult};
|
||||||
|
|
||||||
const DRIVER_NUMBER: usize = 0x50003;
|
const DRIVER_NUMBER: usize = 0x50003;
|
||||||
|
const METADATA_SIGN_OFFSET: usize = 0x800;
|
||||||
|
|
||||||
const UPGRADE_PUBLIC_KEY: &[u8; 65] =
|
const UPGRADE_PUBLIC_KEY: &[u8; 65] =
|
||||||
include_bytes!(concat!(env!("OUT_DIR"), "/opensk_upgrade_pubkey.bin"));
|
include_bytes!(concat!(env!("OUT_DIR"), "/opensk_upgrade_pubkey.bin"));
|
||||||
@@ -100,6 +101,10 @@ fn block_command(driver: usize, cmd: usize, arg1: usize, arg2: usize) -> Storage
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn read_slice(address: usize, length: usize) -> &'static [u8] {
|
||||||
|
core::slice::from_raw_parts(address as *const u8, length)
|
||||||
|
}
|
||||||
|
|
||||||
fn write_slice(ptr: usize, value: &[u8]) -> StorageResult<()> {
|
fn write_slice(ptr: usize, value: &[u8]) -> StorageResult<()> {
|
||||||
let code = unsafe {
|
let code = unsafe {
|
||||||
syscalls::raw::allow(
|
syscalls::raw::allow(
|
||||||
@@ -228,11 +233,15 @@ pub struct TockUpgradeStorage {
|
|||||||
page_size: usize,
|
page_size: usize,
|
||||||
partition: ModRange,
|
partition: ModRange,
|
||||||
metadata: ModRange,
|
metadata: ModRange,
|
||||||
|
running_metadata: ModRange,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TockUpgradeStorage {
|
impl TockUpgradeStorage {
|
||||||
const METADATA_ADDRESS_A: usize = 0x4000;
|
// Ideally, the kernel should tell us metadata and partitions directly.
|
||||||
const METADATA_ADDRESS_B: usize = 0x5000;
|
// This code only works for one layout, refactor this into the storage driver to support more.
|
||||||
|
const METADATA_ADDRESS: usize = 0x4000;
|
||||||
|
const _PARTITION_ADDRESS_A: usize = 0x20000;
|
||||||
|
const PARTITION_ADDRESS_B: usize = 0x60000;
|
||||||
|
|
||||||
/// Provides access to the other upgrade partition and metadata if available.
|
/// Provides access to the other upgrade partition and metadata if available.
|
||||||
///
|
///
|
||||||
@@ -244,7 +253,7 @@ impl TockUpgradeStorage {
|
|||||||
/// Returns `CustomError` if any of the following conditions do not hold:
|
/// Returns `CustomError` if any of the following conditions do not hold:
|
||||||
/// - The page size is a power of two.
|
/// - The page size is a power of two.
|
||||||
/// - The storage slices are page-aligned.
|
/// - The storage slices are page-aligned.
|
||||||
/// - There are not partition or metadata slices.
|
/// - There are no partition or no metadata slices.
|
||||||
/// Returns a `NotAligned` error if partitions or metadata ranges are
|
/// Returns a `NotAligned` error if partitions or metadata ranges are
|
||||||
/// - not exclusive or,
|
/// - not exclusive or,
|
||||||
/// - not consecutive.
|
/// - not consecutive.
|
||||||
@@ -253,6 +262,7 @@ impl TockUpgradeStorage {
|
|||||||
page_size: get_info(command_nr::get_info_nr::PAGE_SIZE, 0)?,
|
page_size: get_info(command_nr::get_info_nr::PAGE_SIZE, 0)?,
|
||||||
partition: ModRange::new_empty(),
|
partition: ModRange::new_empty(),
|
||||||
metadata: ModRange::new_empty(),
|
metadata: ModRange::new_empty(),
|
||||||
|
running_metadata: ModRange::new_empty(),
|
||||||
};
|
};
|
||||||
if !locations.page_size.is_power_of_two() {
|
if !locations.page_size.is_power_of_two() {
|
||||||
return Err(StorageError::CustomError);
|
return Err(StorageError::CustomError);
|
||||||
@@ -269,7 +279,12 @@ impl TockUpgradeStorage {
|
|||||||
}
|
}
|
||||||
let range = ModRange::new(storage_ptr, storage_len);
|
let range = ModRange::new(storage_ptr, storage_len);
|
||||||
match range.start() {
|
match range.start() {
|
||||||
Self::METADATA_ADDRESS_A | Self::METADATA_ADDRESS_B => locations.metadata = range,
|
Self::METADATA_ADDRESS => {
|
||||||
|
// Will be swapped if we are on B.
|
||||||
|
locations.metadata = ModRange::new(range.start(), locations.page_size);
|
||||||
|
locations.running_metadata =
|
||||||
|
ModRange::new(range.start() + locations.page_size, locations.page_size);
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
locations.partition = locations
|
locations.partition = locations
|
||||||
.partition
|
.partition
|
||||||
@@ -278,9 +293,15 @@ impl TockUpgradeStorage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if locations.partition.is_empty() {
|
if locations.partition.is_empty()
|
||||||
|
|| locations.metadata.is_empty()
|
||||||
|
|| locations.running_metadata.is_empty()
|
||||||
|
{
|
||||||
return Err(StorageError::CustomError);
|
return Err(StorageError::CustomError);
|
||||||
}
|
}
|
||||||
|
if locations.partition.start() == Self::PARTITION_ADDRESS_B {
|
||||||
|
core::mem::swap(&mut locations.metadata, &mut locations.running_metadata);
|
||||||
|
}
|
||||||
Ok(locations)
|
Ok(locations)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,7 +320,7 @@ impl UpgradeStorage for TockUpgradeStorage {
|
|||||||
.partition
|
.partition
|
||||||
.contains_range(&ModRange::new(address, length))
|
.contains_range(&ModRange::new(address, length))
|
||||||
{
|
{
|
||||||
Ok(unsafe { core::slice::from_raw_parts(address as *const u8, length) })
|
Ok(unsafe { read_slice(address, length) })
|
||||||
} else {
|
} else {
|
||||||
Err(StorageError::OutOfBounds)
|
Err(StorageError::OutOfBounds)
|
||||||
}
|
}
|
||||||
@@ -332,9 +353,7 @@ impl UpgradeStorage for TockUpgradeStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn read_metadata(&self) -> StorageResult<&[u8]> {
|
fn read_metadata(&self) -> StorageResult<&[u8]> {
|
||||||
Ok(unsafe {
|
Ok(unsafe { read_slice(self.metadata.start(), self.metadata.length()) })
|
||||||
core::slice::from_raw_parts(self.metadata.start() as *const u8, self.metadata.length())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_metadata(&mut self, data: &[u8]) -> StorageResult<()> {
|
fn write_metadata(&mut self, data: &[u8]) -> StorageResult<()> {
|
||||||
@@ -348,6 +367,16 @@ impl UpgradeStorage for TockUpgradeStorage {
|
|||||||
}
|
}
|
||||||
write_slice(self.metadata.start(), data)
|
write_slice(self.metadata.start(), data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn running_firmware_version(&self) -> u64 {
|
||||||
|
let running_metadata = unsafe {
|
||||||
|
read_slice(
|
||||||
|
self.running_metadata.start(),
|
||||||
|
self.running_metadata.length(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
parse_metadata_version(running_metadata)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the metadata of an upgrade, and checks its correctness.
|
/// Parses the metadata of an upgrade, and checks its correctness.
|
||||||
@@ -367,11 +396,15 @@ fn parse_metadata(
|
|||||||
metadata: &[u8],
|
metadata: &[u8],
|
||||||
) -> StorageResult<()> {
|
) -> StorageResult<()> {
|
||||||
const METADATA_LEN: usize = 0x1000;
|
const METADATA_LEN: usize = 0x1000;
|
||||||
const METADATA_SIGN_OFFSET: usize = 0x800;
|
|
||||||
if metadata.len() != METADATA_LEN {
|
if metadata.len() != METADATA_LEN {
|
||||||
return Err(StorageError::CustomError);
|
return Err(StorageError::CustomError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let version = parse_metadata_version(metadata);
|
||||||
|
if version < upgrade_locations.running_firmware_version() {
|
||||||
|
return Err(StorageError::CustomError);
|
||||||
|
}
|
||||||
|
|
||||||
let metadata_address = LittleEndian::read_u32(&metadata[METADATA_SIGN_OFFSET + 8..][..4]);
|
let metadata_address = LittleEndian::read_u32(&metadata[METADATA_SIGN_OFFSET + 8..][..4]);
|
||||||
if metadata_address as usize != upgrade_locations.partition_address() {
|
if metadata_address as usize != upgrade_locations.partition_address() {
|
||||||
return Err(StorageError::CustomError);
|
return Err(StorageError::CustomError);
|
||||||
@@ -396,6 +429,11 @@ fn parse_metadata(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses the metadata, returns the firmware version.
|
||||||
|
fn parse_metadata_version(data: &[u8]) -> u64 {
|
||||||
|
LittleEndian::read_u64(&data[METADATA_SIGN_OFFSET..][..8])
|
||||||
|
}
|
||||||
|
|
||||||
/// Verifies the signature over the given hash.
|
/// Verifies the signature over the given hash.
|
||||||
///
|
///
|
||||||
/// The public key is COSE encoded, and the hash is a SHA256.
|
/// The public key is COSE encoded, and the hash is a SHA256.
|
||||||
|
|||||||
@@ -198,6 +198,13 @@ def main(args):
|
|||||||
aaguid = uuid.UUID(bytes=authenticator.get_info().aaguid)
|
aaguid = uuid.UUID(bytes=authenticator.get_info().aaguid)
|
||||||
info(f"Upgrading OpenSK device AAGUID {aaguid} ({authenticator.device}).")
|
info(f"Upgrading OpenSK device AAGUID {aaguid} ({authenticator.device}).")
|
||||||
|
|
||||||
|
running_version = authenticator.get_info().firmware_version
|
||||||
|
if args.version < running_version:
|
||||||
|
fatal(f"Can not write version {args.version} when version"
|
||||||
|
f"{running_version} is running.")
|
||||||
|
else:
|
||||||
|
info(f"Running version: {running_version}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
check_info(partition_address, authenticator)
|
check_info(partition_address, authenticator)
|
||||||
offset = 0
|
offset = 0
|
||||||
@@ -225,7 +232,7 @@ def main(args):
|
|||||||
elif ex.code.value == ctap.CtapError.ERR.INVALID_PARAMETER:
|
elif ex.code.value == ctap.CtapError.ERR.INVALID_PARAMETER:
|
||||||
error(f"{message} (invalid parameter, maybe a wrong byte array size?).")
|
error(f"{message} (invalid parameter, maybe a wrong byte array size?).")
|
||||||
elif ex.code.value == ctap.CtapError.ERR.INTEGRITY_FAILURE:
|
elif ex.code.value == ctap.CtapError.ERR.INTEGRITY_FAILURE:
|
||||||
error(f"{message} (hashes or signature don't match).")
|
error(f"{message} (metadata parsing failed).")
|
||||||
elif ex.code.value == 0xF2: # VENDOR_INTERNAL_ERROR
|
elif ex.code.value == 0xF2: # VENDOR_INTERNAL_ERROR
|
||||||
error(f"{message} (internal conditions not met).")
|
error(f"{message} (internal conditions not met).")
|
||||||
elif ex.code.value == 0xF3: # VENDOR_HARDWARE_FAILURE
|
elif ex.code.value == 0xF3: # VENDOR_HARDWARE_FAILURE
|
||||||
|
|||||||
Reference in New Issue
Block a user