Skip to content

Commit

Permalink
squash! staging
Browse files Browse the repository at this point in the history
  • Loading branch information
jeandudey committed May 9, 2024
1 parent d3727fe commit d5646c6
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 44 deletions.
37 changes: 7 additions & 30 deletions extmod/foundation-rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion extmod/foundation-rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ branch = "jeandudey/sft-3538-add-foundation-firmware-crate-to-parse-firmware-ima
default-features = false

[dependencies.foundation-ur]
version = "0.1"
version = "0.2"
git = "https://github.com/Foundation-Devices/foundation-rs"
branch = "jeandudey/sft-3538-add-foundation-firmware-crate-to-parse-firmware-images"
default-features = false

[dependencies.foundation-urtypes]
Expand Down
28 changes: 25 additions & 3 deletions extmod/foundation-rust/include/foundation.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ typedef enum {
/**
* The firmware validation succeed.
*/
FIRMWARE_RESULT_OK,
FIRMWARE_RESULT_HEADER_OK,
/**
* The header format is not valid.
*/
Expand Down Expand Up @@ -154,12 +154,28 @@ typedef enum {
* The same public key was used for the two signatures.
*/
FIRMWARE_RESULT_SAME_PUBLIC_KEY,
/**
* Signature verification succeed.
*/
FIRMWARE_RESULT_SIGNATURES_OK,
/**
* The user signed firmware is not valid.
*/
FIRMWARE_RESULT_INVALID_USER_SIGNATURE,
/**
* The first signature verification failed.
*/
FIRMWARE_RESULT_FAILED_SIGNATURE1,
/**
* The second signature verification failed.
*/
FIRMWARE_RESULT_FAILED_SIGNATURE2,
} FirmwareResult_Tag;

typedef struct {
char version[VERSION_LEN];
bool signed_by_user;
} FirmwareResult_Ok_Body;
} FirmwareResult_HeaderOk_Body;

typedef struct {
uint32_t magic;
Expand Down Expand Up @@ -195,7 +211,7 @@ typedef struct {
typedef struct {
FirmwareResult_Tag tag;
union {
FirmwareResult_Ok_Body OK;
FirmwareResult_HeaderOk_Body HEADER_OK;
FirmwareResult_UnknownMagic_Body UNKNOWN_MAGIC;
FirmwareResult_TooSmall_Body TOO_SMALL;
FirmwareResult_TooBig_Body TOO_BIG;
Expand Down Expand Up @@ -466,6 +482,12 @@ void foundation_firmware_verify_update_header(const uint8_t *header,
uint32_t current_timestamp,
FirmwareResult *result);

void foundation_firmware_verify_update_signatures(const uint8_t *header,
size_t header_len,
uint32_t current_timestamp,
const uint8_t (*hash)[32],
FirmwareResult *result);

/**
* Computes a Schnorr signature over the message `data`.
*
Expand Down
53 changes: 47 additions & 6 deletions extmod/foundation-rust/src/firmware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use crate::secp256k1::PRE_ALLOCATED_CTX;
use bitcoin_hashes::{sha256d, Hash};
use core::{ffi::c_char, slice};
use foundation_firmware::VerifyHeaderError;
use foundation_firmware::{VerifyHeaderError, VerifySignatureError};

pub const VERSION_LEN: usize = 8;

Expand All @@ -13,8 +13,9 @@ pub const VERSION_LEN: usize = 8;
/// cbindgen:prefix-with-name
#[repr(C)]
pub enum FirmwareResult {
// Header.
/// The firmware validation succeed.
Ok {
HeaderOk {
version: [c_char; VERSION_LEN],
signed_by_user: bool,
},
Expand Down Expand Up @@ -42,6 +43,15 @@ pub enum FirmwareResult {
/// Index of the duplicated key.
index: u32,
},
// Signatures.
/// Signature verification succeed.
SignaturesOk,
/// The user signed firmware is not valid.
InvalidUserSignature,
/// The first signature verification failed.
FailedSignature1,
/// The second signature verification failed.
FailedSignature2,
}

impl From<VerifyHeaderError> for FirmwareResult {
Expand All @@ -64,6 +74,23 @@ impl From<VerifyHeaderError> for FirmwareResult {
}
}

impl From<VerifySignatureError> for FirmwareResult {
fn from(e: VerifySignatureError) -> Self {
use FirmwareResult::*;

match e {
VerifySignatureError::InvalidUserSignature { .. } => {
InvalidUserSignature
}
VerifySignatureError::FailedSignature1 { .. } => FailedSignature1,
VerifySignatureError::FailedSignature2 { .. } => FailedSignature2,
// No need to implement this, see comment on
// verify_update_signatures.
VerifySignatureError::MissingUserPublicKey => unimplemented!(),
}
}
}

fn verify_update_header_impl(
header: &[u8],
current_timestamp: u32,
Expand Down Expand Up @@ -111,7 +138,7 @@ pub extern "C" fn verify_update_header(
}
version[version_bytes.len()] = b'\0' as c_char;

*result = FirmwareResult::Ok {
*result = FirmwareResult::HeaderOk {
version,
signed_by_user: header.is_signed_by_user(),
};
Expand All @@ -121,6 +148,7 @@ pub extern "C" fn verify_update_header(
}
}

#[export_name = "foundation_firmware_verify_update_signatures"]
pub extern "C" fn verify_update_signatures(
header: *const u8,
header_len: usize,
Expand All @@ -138,13 +166,26 @@ pub extern "C" fn verify_update_signatures(
None => return,
};

foundation_firmware::verify_signature(
match foundation_firmware::verify_signature(
&PRE_ALLOCATED_CTX,
&header,
&firmware_hash,
None,
)
.unwrap();
) {
Ok(()) => {
*result = FirmwareResult::SignaturesOk;
}
// The code calling this function must make sure that there's an user
// public key provided to us before verifying signatures.
//
// When verifying signatures is because we are committed to the
// update, i.e. the user has accepted to do it, so we must have
// presented an error if there was not an user public key earlier.
Err(VerifySignatureError::MissingUserPublicKey) => {
unreachable!("we always provide a user public key")
}
Err(e) => *result = FirmwareResult::from(e),
}
}

#[cfg(test)]
Expand Down
91 changes: 87 additions & 4 deletions ports/stm32/boards/Passport/modpassport.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,11 @@ STATIC mp_obj_t mod_passport_verify_update_header(mp_obj_t header) {
&result);

switch (result.tag) {
case FIRMWARE_RESULT_OK:
case FIRMWARE_RESULT_HEADER_OK:
tuple[0] = mp_obj_new_str_copy(&mp_type_str,
(const uint8_t*)result.OK.version,
strlen((const char*)result.OK.version));
tuple[1] = result.OK.signed_by_user ? mp_const_true : mp_const_false;
(const uint8_t*)result.HEADER_OK.version,
strlen((const char*)result.HEADER_OK.version));
tuple[1] = result.HEADER_OK.signed_by_user ? mp_const_true : mp_const_false;
return mp_obj_new_tuple(2, tuple);
case FIRMWARE_RESULT_INVALID_HEADER:
mp_raise_msg(&mp_type_InvalidFirmwareUpdate,
Expand Down Expand Up @@ -149,6 +149,89 @@ STATIC mp_obj_t mod_passport_verify_update_signatures(mp_obj_t header, mp_obj_t
mp_buffer_info_t header_info;
mp_get_buffer_raise(header, &header_info, MP_BUFFER_READ);

mp_buffer_info_t validation_hash_info;
mp_get_buffer_raise(validation_hash, &validation_hash_info, MP_BUFFER_READ);

// Build the current board hash so we can get the minimum firmware
// timestamp
uint8_t current_board_hash[HASH_LEN] = {0};
get_current_board_hash(current_board_hash);

uint32_t firmware_timestamp = se_get_firmware_timestamp(current_board_hash);

// TODO: This one causes text to overflow.
FirmwareResult result = {0};
foundation_firmware_verify_update_signatures(header_info.buf,
header_info.len,
firmware_timestamp,
validation_hash_info.buf,
&result);

// // Signature verification also validates the header again, so, we have
// // to handle the errors again.
// //
// // TODO: This should be factored out.
// switch (result.tag) {
// case FIRMWARE_RESULT_SIGNATURES_OK:
// return mp_const_true;
// case FIRMWARE_RESULT_INVALID_HEADER:
// mp_raise_msg(&mp_type_InvalidFirmwareUpdate,
// MP_ERROR_TEXT("Invalid firmware header"));
// case FIRMWARE_RESULT_UNKNOWN_MAGIC:
// mp_raise_msg(&mp_type_InvalidFirmwareUpdate,
// MP_ERROR_TEXT("Unknown firmware magic bytes"));
// break;
// case FIRMWARE_RESULT_INVALID_TIMESTAMP:
// mp_raise_msg(&mp_type_InvalidFirmwareUpdate,
// MP_ERROR_TEXT("Invalid firmware timestamp"));
// break;
// case FIRMWARE_RESULT_TOO_SMALL:
// mp_raise_msg_varg(&mp_type_InvalidFirmwareUpdate,
// MP_ERROR_TEXT("Firmware size is too small: %"PRIu32" bytes."),
// result.TOO_SMALL.len);
// break;
// case FIRMWARE_RESULT_TOO_BIG:
// mp_raise_msg_varg(&mp_type_InvalidFirmwareUpdate,
// MP_ERROR_TEXT("Firmware size is too big: %"PRIu32" bytes."),
// result.TOO_BIG.len);
// break;
// case FIRMWARE_RESULT_TOO_OLD:
// mp_raise_msg_varg(&mp_type_InvalidFirmwareUpdate,
// MP_ERROR_TEXT("Firmware is older than current, timestamp is: %"PRIu32"."),
// result.TOO_OLD.timestamp);
// break;
// case FIRMWARE_RESULT_INVALID_PUBLIC_KEY1_INDEX:
// mp_raise_msg_varg(&mp_type_InvalidFirmwareUpdate,
// MP_ERROR_TEXT("Public Key is out of range (%"PRIu32")."),
// result.INVALID_PUBLIC_KEY1_INDEX.index);
// break;
// case FIRMWARE_RESULT_INVALID_PUBLIC_KEY2_INDEX:
// mp_raise_msg_varg(&mp_type_InvalidFirmwareUpdate,
// MP_ERROR_TEXT("Public Key is out of range (%"PRIu32")."),
// result.INVALID_PUBLIC_KEY2_INDEX.index);
// break;
// case FIRMWARE_RESULT_SAME_PUBLIC_KEY:
// mp_raise_msg_varg(&mp_type_InvalidFirmwareUpdate,
// MP_ERROR_TEXT("Same Public Key was used for both signatures (%"PRIu32")."),
// result.SAME_PUBLIC_KEY.index);
// break;
// case FIRMWARE_RESULT_INVALID_USER_SIGNATURE:
// mp_raise_msg(&mp_type_InvalidFirmwareUpdate,
// MP_ERROR_TEXT("User signature verification failed"));
// break;
// case FIRMWARE_RESULT_FAILED_SIGNATURE1:
// mp_raise_msg(&mp_type_InvalidFirmwareUpdate,
// MP_ERROR_TEXT("First signature verification failed"));
// case FIRMWARE_RESULT_FAILED_SIGNATURE2:
// mp_raise_msg(&mp_type_InvalidFirmwareUpdate,
// MP_ERROR_TEXT("Second signature verification failed"));
// default:
// break;
// }


mp_raise_msg(&mp_type_InvalidFirmwareUpdate,
MP_ERROR_TEXT("Unhandled case in firmware signatures verification"));
return mp_const_false;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_passport_verify_update_signatures_obj,
Expand Down

0 comments on commit d5646c6

Please sign in to comment.