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
fi
GIT_INFO=""
if [ ! -z "${GIT_BRANCH}" ]; then GIT_INFO+="branch: [$GIT_BRANCH] "; fi
if [ ! -z "${GIT_TAG}" ]; then GIT_INFO+="tag: [$GIT_TAG] "; fi
if [ ! -z "${GIT_SHA}" ]; then GIT_INFO+="sha: [$GIT_SHA] "; fi
if [ ! -z "${GIT_MESSAGE}" ]; then GIT_INFO+="message: [$GIT_MESSAGE] "; fi
GIT_INFO=$(echo "$GIT_INFO" | xargs)
if [ ! -z "${GIT_BRANCH}" ]; then GIT_INFO+=$'\n'"branch: $GIT_BRANCH"; fi
if [ ! -z "${GIT_TAG}" ]; then GIT_INFO+=$'\n'"tag: $GIT_TAG"; fi
if [ ! -z "${GIT_SHA}" ]; then GIT_INFO+=$'\n'"sha: $GIT_SHA"; fi
if [ ! -z "${GIT_MESSAGE}" ]; then GIT_INFO+=$'\n'"commit msg: $GIT_MESSAGE"; fi
python3 update.py \
--git "$GIT_INFO" \
--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 {
pub fn handle_debug_packet(&mut self, debug_packet: sc64::DebugPacket) {
let sc64::DebugPacket { datatype, data } = debug_packet;
@ -97,13 +103,13 @@ impl Handler {
println!("Invalid header length for screenshot datatype");
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");
return;
}
let pixel_format: u32 = u32::from_be_bytes(header[4..8].try_into().unwrap());
let width: u32 = u32::from_be_bytes(header[8..12].try_into().unwrap());
let height: u32 = u32::from_be_bytes(header[12..16].try_into().unwrap());
let pixel_format = u32::from_be_bytes(header[4..8].try_into().unwrap());
let width = u32::from_be_bytes(header[8..12].try_into().unwrap());
let height = u32::from_be_bytes(header[12..16].try_into().unwrap());
if pixel_format != 2 && pixel_format != 4 {
println!("Invalid pixel format for screenshot datatype");
return;
@ -141,7 +147,9 @@ impl Handler {
let filename = &self.generate_filename("screenshot", "png");
if let Some(error) = image.save(filename).err() {
println!("Error during image save: {error}");
return;
}
println!("Wrote {width}x{height} pixels to [{filename}]");
} else {
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];
firmware_file.read_exact(&mut firmware)?;
// TODO: print firmware metadata
println!("{}", "Sorry nothing".yellow());
let metadata = sc64::firmware::verify(&firmware)?;
println!("{}", "Firmware metadata:".bold());
println!("{}", metadata);
Ok(())
}
@ -463,12 +463,16 @@ fn handle_firmware_command(
let (mut backup_file, backup_name) = create_file(&args.firmware)?;
sc64.reset_state()?;
let firmware = log_wait(
format!("Generating firmware backup, this might take a while [{backup_name}]"),
|| sc64.backup_firmware(),
)?;
// TODO: print firmware metadata
let metadata = sc64::firmware::verify(&firmware)?;
println!("{}", "Firmware metadata:".bold());
println!("{}", metadata);
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 fimware = vec![0u8; update_length as usize];
update_file.read_exact(&mut fimware)?;
let mut firmware = vec![0u8; update_length as usize];
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(
format!("Updating firmware, this might take a while [{update_name}]"),
|| sc64.update_firmware(&fimware),
|| sc64.update_firmware(&firmware),
)?;
Ok(())

View File

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

View File

@ -1,5 +1,4 @@
use super::Error;
use crc32fast::Hasher;
pub const IPL3_OFFSET: u32 = 0x40;
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"));
}
let mut hasher = Hasher::new();
hasher.update(ipl3);
let cic_type: CicType = hasher.finalize().into();
let cic_type: CicType = crc32fast::hash(ipl3).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 error;
pub mod firmware;
mod link;
mod types;
mod utils;

View File

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