firmware verification

This commit is contained in:
Mateusz Faderewski 2023-03-04 01:25:16 +01:00
parent 0b7ba63184
commit a0a6423b92
8 changed files with 190 additions and 31 deletions

View File

@ -89,11 +89,10 @@ build_update () {
rm -f ./sc64-firmware.bin rm -f ./sc64-firmware.bin
fi fi
GIT_INFO="" GIT_INFO=""
if [ ! -z "${GIT_BRANCH}" ]; then GIT_INFO+="branch: [$GIT_BRANCH] "; fi if [ ! -z "${GIT_BRANCH}" ]; then GIT_INFO+=$'\n'"branch: $GIT_BRANCH"; fi
if [ ! -z "${GIT_TAG}" ]; then GIT_INFO+="tag: [$GIT_TAG] "; fi if [ ! -z "${GIT_TAG}" ]; then GIT_INFO+=$'\n'"tag: $GIT_TAG"; fi
if [ ! -z "${GIT_SHA}" ]; then GIT_INFO+="sha: [$GIT_SHA] "; fi if [ ! -z "${GIT_SHA}" ]; then GIT_INFO+=$'\n'"sha: $GIT_SHA"; fi
if [ ! -z "${GIT_MESSAGE}" ]; then GIT_INFO+="message: [$GIT_MESSAGE] "; fi if [ ! -z "${GIT_MESSAGE}" ]; then GIT_INFO+=$'\n'"commit msg: $GIT_MESSAGE"; fi
GIT_INFO=$(echo "$GIT_INFO" | xargs)
python3 update.py \ python3 update.py \
--git "$GIT_INFO" \ --git "$GIT_INFO" \
--mcu ../controller/build/app/app.bin \ --mcu ../controller/build/app/app.bin \

View File

@ -53,6 +53,12 @@ impl From<DataType> for u8 {
} }
} }
impl From<DataType> for u32 {
fn from(value: DataType) -> Self {
u8::from(value) as u32
}
}
impl Handler { impl Handler {
pub fn handle_debug_packet(&mut self, debug_packet: sc64::DebugPacket) { pub fn handle_debug_packet(&mut self, debug_packet: sc64::DebugPacket) {
let sc64::DebugPacket { datatype, data } = debug_packet; let sc64::DebugPacket { datatype, data } = debug_packet;
@ -97,13 +103,13 @@ impl Handler {
println!("Invalid header length for screenshot datatype"); println!("Invalid header length for screenshot datatype");
return; return;
} }
if u32::from_be_bytes(header[0..4].try_into().unwrap()) != DataType::Screenshot as u32 { if u32::from_be_bytes(header[0..4].try_into().unwrap()) != DataType::Screenshot.into() {
println!("Invalid header datatype for screenshot datatype"); println!("Invalid header datatype for screenshot datatype");
return; return;
} }
let pixel_format: u32 = u32::from_be_bytes(header[4..8].try_into().unwrap()); let pixel_format = u32::from_be_bytes(header[4..8].try_into().unwrap());
let width: u32 = u32::from_be_bytes(header[8..12].try_into().unwrap()); let width = u32::from_be_bytes(header[8..12].try_into().unwrap());
let height: u32 = u32::from_be_bytes(header[12..16].try_into().unwrap()); let height = u32::from_be_bytes(header[12..16].try_into().unwrap());
if pixel_format != 2 && pixel_format != 4 { if pixel_format != 2 && pixel_format != 4 {
println!("Invalid pixel format for screenshot datatype"); println!("Invalid pixel format for screenshot datatype");
return; return;
@ -141,7 +147,9 @@ impl Handler {
let filename = &self.generate_filename("screenshot", "png"); let filename = &self.generate_filename("screenshot", "png");
if let Some(error) = image.save(filename).err() { if let Some(error) = image.save(filename).err() {
println!("Error during image save: {error}"); println!("Error during image save: {error}");
return;
} }
println!("Wrote {width}x{height} pixels to [{filename}]");
} else { } else {
println!("Got screenshot packet without header data"); println!("Got screenshot packet without header data");
} }

View File

@ -451,9 +451,9 @@ fn handle_firmware_command(
let mut firmware = vec![0u8; firmware_length as usize]; let mut firmware = vec![0u8; firmware_length as usize];
firmware_file.read_exact(&mut firmware)?; firmware_file.read_exact(&mut firmware)?;
// TODO: print firmware metadata let metadata = sc64::firmware::verify(&firmware)?;
println!("{}", "Firmware metadata:".bold());
println!("{}", "Sorry nothing".yellow()); println!("{}", metadata);
Ok(()) Ok(())
} }
@ -463,12 +463,16 @@ fn handle_firmware_command(
let (mut backup_file, backup_name) = create_file(&args.firmware)?; let (mut backup_file, backup_name) = create_file(&args.firmware)?;
sc64.reset_state()?;
let firmware = log_wait( let firmware = log_wait(
format!("Generating firmware backup, this might take a while [{backup_name}]"), format!("Generating firmware backup, this might take a while [{backup_name}]"),
|| sc64.backup_firmware(), || sc64.backup_firmware(),
)?; )?;
// TODO: print firmware metadata let metadata = sc64::firmware::verify(&firmware)?;
println!("{}", "Firmware metadata:".bold());
println!("{}", metadata);
backup_file.write(&firmware)?; backup_file.write(&firmware)?;
@ -480,14 +484,18 @@ fn handle_firmware_command(
let (mut update_file, update_name, update_length) = open_file(&args.firmware)?; let (mut update_file, update_name, update_length) = open_file(&args.firmware)?;
let mut fimware = vec![0u8; update_length as usize]; let mut firmware = vec![0u8; update_length as usize];
update_file.read_exact(&mut fimware)?; update_file.read_exact(&mut firmware)?;
// TODO: print firmware metadata let metadata = sc64::firmware::verify(&firmware)?;
println!("{}", "Firmware metadata:".bold());
println!("{}", metadata);
sc64.reset_state()?;
log_wait( log_wait(
format!("Updating firmware, this might take a while [{update_name}]"), format!("Updating firmware, this might take a while [{update_name}]"),
|| sc64.update_firmware(&fimware), || sc64.update_firmware(&firmware),
)?; )?;
Ok(()) Ok(())

View File

@ -1,4 +1,4 @@
use std::io::{Error, Read, Seek}; use std::io::{Error, Read, Seek, SeekFrom};
pub enum SaveType { pub enum SaveType {
None, None,
@ -16,7 +16,7 @@ pub fn guess_save_type<T: Read + Seek>(
) -> Result<(SaveType, Option<String>), Error> { ) -> Result<(SaveType, Option<String>), Error> {
let mut ed64_header = vec![0u8; 4]; let mut ed64_header = vec![0u8; 4];
reader.seek(std::io::SeekFrom::Start(0x3C))?; reader.seek(SeekFrom::Start(0x3C))?;
reader.read(&mut ed64_header)?; reader.read(&mut ed64_header)?;
if &ed64_header[0..2] == b"ED" { if &ed64_header[0..2] == b"ED" {

View File

@ -1,5 +1,4 @@
use super::Error; use super::Error;
use crc32fast::Hasher;
pub const IPL3_OFFSET: u32 = 0x40; pub const IPL3_OFFSET: u32 = 0x40;
pub const IPL3_LENGTH: usize = 0xFC0; pub const IPL3_LENGTH: usize = 0xFC0;
@ -66,11 +65,7 @@ pub fn guess_ipl3_seed(ipl3: &[u8]) -> Result<u8, Error> {
return Err(Error::new("Invalid IPL3 length provided")); return Err(Error::new("Invalid IPL3 length provided"));
} }
let mut hasher = Hasher::new(); let cic_type: CicType = crc32fast::hash(ipl3).into();
hasher.update(ipl3);
let cic_type: CicType = hasher.finalize().into();
Ok(cic_type.into()) Ok(cic_type.into())
} }

View File

@ -0,0 +1,149 @@
use super::Error;
use std::{
fmt::Display,
io::{Cursor, Read, Seek, SeekFrom},
};
enum ChunkId {
UpdateInfo,
McuData,
FpgaData,
BootloaderData,
PrimerData,
}
impl TryFrom<u32> for ChunkId {
type Error = Error;
fn try_from(value: u32) -> Result<Self, Self::Error> {
Ok(match value {
1 => Self::UpdateInfo,
2 => Self::McuData,
3 => Self::FpgaData,
4 => Self::BootloaderData,
5 => Self::PrimerData,
_ => return Err(Error::new("Unknown chunk id inside firmware update file")),
})
}
}
impl Display for ChunkId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
ChunkId::UpdateInfo => "Update info",
ChunkId::McuData => "MCU data",
ChunkId::FpgaData => "FPGA data",
ChunkId::BootloaderData => "Bootloader data",
ChunkId::PrimerData => "Primer data",
})
}
}
pub struct Firmware {
update_info: Option<String>,
mcu_data: Option<Vec<u8>>,
fpga_data: Option<Vec<u8>>,
bootloader_data: Option<Vec<u8>>,
primer_data: Option<Vec<u8>>,
}
impl Display for Firmware {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(update_info) = &self.update_info {
f.write_fmt(format_args!("{}", &update_info))?;
} else {
f.write_str("No update info data included")?;
}
if let Some(data) = &self.mcu_data {
f.write_fmt(format_args!(
"\nMCU data present, length: 0x{:X}",
data.len()
))?;
} else {
f.write_str("\nNo MCU data included")?;
}
if let Some(data) = &self.fpga_data {
f.write_fmt(format_args!(
"\nFPGA data present, length: 0x{:X}",
data.len()
))?;
} else {
f.write_str("\nNo FPGA data included")?;
}
if let Some(data) = &self.bootloader_data {
f.write_fmt(format_args!(
"\nBootloader data present, length: 0x{:X}",
data.len()
))?;
} else {
f.write_str("\nNo bootloader data included")?;
}
if let Some(data) = &self.primer_data {
f.write_fmt(format_args!(
"\nPrimer data present, length: 0x{:X}",
data.len()
))?;
} else {
f.write_str("\nNo primer data included")?;
}
Ok(())
}
}
const SC64_FIRMWARE_UPDATE_TOKEN: &[u8; 16] = b"SC64 Update v2.0";
pub fn verify(data: &[u8]) -> Result<Firmware, Error> {
let mut buffer = vec![0u8; 16];
let mut reader = Cursor::new(data);
reader.read(&mut buffer)?;
if buffer != SC64_FIRMWARE_UPDATE_TOKEN {
return Err(Error::new("Invalid firmware update header"));
}
let mut firmware = Firmware {
update_info: None,
mcu_data: None,
fpga_data: None,
bootloader_data: None,
primer_data: None,
};
loop {
let bytes = reader.read(&mut buffer)?;
if bytes == 16 {
let id: ChunkId = u32::from_le_bytes(buffer[0..4].try_into().unwrap()).try_into()?;
let aligned_length = u32::from_le_bytes(buffer[4..8].try_into().unwrap());
let checksum = u32::from_le_bytes(buffer[8..12].try_into().unwrap());
let data_length = u32::from_le_bytes(buffer[12..16].try_into().unwrap());
let mut data = vec![0u8; data_length as usize];
reader.read(&mut data)?;
let align = aligned_length - 4 - 4 - data_length;
reader.seek(SeekFrom::Current(align as i64))?;
if crc32fast::hash(&data) != checksum {
return Err(Error::new(
format!("Invalid checksum for chunk [{id}]").as_str(),
));
}
match id {
ChunkId::UpdateInfo => {
firmware.update_info = Some(String::from_utf8_lossy(&data).to_string())
}
ChunkId::McuData => firmware.mcu_data = Some(data),
ChunkId::FpgaData => firmware.fpga_data = Some(data),
ChunkId::BootloaderData => firmware.bootloader_data = Some(data),
ChunkId::PrimerData => firmware.primer_data = Some(data),
}
} else if bytes == 0 {
break;
} else {
return Err(Error::new("Unexpected end of data in firmware update"));
}
}
Ok(firmware)
}

View File

@ -1,5 +1,6 @@
mod cic; mod cic;
mod error; mod error;
pub mod firmware;
mod link; mod link;
mod types; mod types;
mod utils; mod utils;

View File

@ -219,13 +219,12 @@ if __name__ == "__main__":
hostname = platform.node() hostname = platform.node()
creation_datetime = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S') creation_datetime = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
info = [ update_info = '\n'.join([
f'build system: [{hostname}]', f'build system: {hostname}',
f'creation datetime: [{creation_datetime}]', f'creation datetime: {creation_datetime}',
] ])
if (args.git): if (args.git):
info.append(args.git) update_info += args.git
update_info = ' '.join(info)
print(update_info) print(update_info)
update.add_update_info(update_info.encode()) update.add_update_info(update_info.encode())