mirror of
https://github.com/Astatin3/CC2.git
synced 2026-06-08 16:08:00 -06:00
Add sig strip command
This commit is contained in:
@@ -4,3 +4,6 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
aes = "0.8.4"
|
||||||
|
cbc = "0.1.2"
|
||||||
|
clap = { version = "4.5.37", features = ["derive"] }
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
//! Clap command and argument definitions.
|
||||||
|
//!
|
||||||
|
//! This module is deliberately limited to command-line shape: subcommand names,
|
||||||
|
//! flags, positional arguments, and user-facing help text. It does not know how
|
||||||
|
//! to parse or decrypt `.sig` files. Keeping those responsibilities out of the
|
||||||
|
//! CLI layer makes the format code easier to test and reuse from future commands.
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use clap::{Args, Parser, Subcommand};
|
||||||
|
|
||||||
|
/// Top-level command-line parser for the `sig` binary.
|
||||||
|
///
|
||||||
|
/// `clap` derives `--help`, `--version`, shell-friendly error messages, and the
|
||||||
|
/// dispatch table for all subcommands from this struct. New subcommands should
|
||||||
|
/// be added to [`Command`] rather than handled manually in `main`.
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(version, about = "Interact with Centauri Carbon 2 .sig packages")]
|
||||||
|
pub struct Cli {
|
||||||
|
/// The action to run.
|
||||||
|
#[command(subcommand)]
|
||||||
|
pub command: Command,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Supported subcommands.
|
||||||
|
///
|
||||||
|
/// The tool currently has one operation: `strip`. The enum layout leaves room
|
||||||
|
/// for future package inspection, signing, or repacking commands without
|
||||||
|
/// changing the top-level parser API.
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
pub enum Command {
|
||||||
|
/// Strip the .sig wrapper and write the unpacked payload.
|
||||||
|
Strip(StripArgs),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Arguments accepted by the `strip` subcommand.
|
||||||
|
///
|
||||||
|
/// The command mirrors the browser-based unpacker at
|
||||||
|
/// <https://docs.opencentauri.cc/extras/cc2_update_decrypt.html>: read a `.sig`
|
||||||
|
/// file, remove the header, decrypt the payload when needed, trim it to the
|
||||||
|
/// payload size declared in the header, then write the resulting package bytes.
|
||||||
|
#[derive(Args)]
|
||||||
|
pub struct StripArgs {
|
||||||
|
/// Input `.sig` file.
|
||||||
|
///
|
||||||
|
/// The file must begin with the Centauri/Elegoo `ELEG` magic value and must
|
||||||
|
/// contain the 512-byte header used by Carbon 2 update packages.
|
||||||
|
pub input: PathBuf,
|
||||||
|
|
||||||
|
/// Output path.
|
||||||
|
///
|
||||||
|
/// When omitted, the default output is the input filename with a trailing
|
||||||
|
/// `.sig` extension removed. For example, `update.swu.sig` writes
|
||||||
|
/// `update.swu`.
|
||||||
|
#[arg(short, long)]
|
||||||
|
pub output: Option<PathBuf>,
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
//! Centauri Carbon 2 `.sig` package parsing and AES-CBC decryption.
|
||||||
|
//!
|
||||||
|
//! The Carbon 2 update `.sig` container starts with a fixed 512-byte header.
|
||||||
|
//! The header begins with the big-endian magic value `ELEG`, carries a package
|
||||||
|
//! type byte whose high bit marks encrypted payloads, stores the unwrapped
|
||||||
|
//! payload size as a little-endian `u64`, and stores the AES-CBC IV at offset
|
||||||
|
//! `0xA0`. The wrapped payload starts immediately after the header.
|
||||||
|
//!
|
||||||
|
//! This module implements the same unpacking behavior as the public web tool at
|
||||||
|
//! <https://docs.opencentauri.cc/extras/cc2_update_decrypt.html>. It does not
|
||||||
|
//! attempt to validate signatures or hashes yet; it only removes the wrapper and
|
||||||
|
//! decrypts encrypted payloads.
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
use aes::Aes256;
|
||||||
|
use cbc::cipher::{BlockDecryptMut, KeyIvInit, block_padding::NoPadding};
|
||||||
|
|
||||||
|
/// Length of the fixed `.sig` header in bytes.
|
||||||
|
pub const HEADER_LEN: usize = 512;
|
||||||
|
|
||||||
|
/// Big-endian `ELEG` magic value found at the beginning of supported packages.
|
||||||
|
const MAGIC: u32 = 0x454C4547;
|
||||||
|
|
||||||
|
/// AES-256-CBC key used by Carbon 2 update packages.
|
||||||
|
///
|
||||||
|
/// This key is taken from the referenced browser implementation. The IV is not
|
||||||
|
/// fixed; each package supplies its IV in the `.sig` header at bytes
|
||||||
|
/// `0xA0..0xB0`.
|
||||||
|
const AES_KEY: [u8; 32] = [
|
||||||
|
0xD1, 0x4E, 0x15, 0x08, 0x43, 0xE9, 0xD1, 0x68, 0x93, 0x89, 0x07, 0x56, 0xD8, 0xF7, 0x7F, 0x67,
|
||||||
|
0x4E, 0x16, 0x1A, 0x8B, 0xEB, 0xB8, 0xF7, 0x20, 0x73, 0x7E, 0xE6, 0x0E, 0x7F, 0x8C, 0x7E, 0x68,
|
||||||
|
];
|
||||||
|
|
||||||
|
type Aes256CbcDec = cbc::Decryptor<Aes256>;
|
||||||
|
|
||||||
|
/// Parsed subset of a Carbon 2 `.sig` header.
|
||||||
|
///
|
||||||
|
/// The header contains additional metadata such as package type, version,
|
||||||
|
/// encrypted byte ranges, and SHA-256 material. `strip` only needs the fields
|
||||||
|
/// represented here: whether the payload is encrypted, how many bytes should be
|
||||||
|
/// emitted after unwrap, and which IV to use for AES-CBC.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Header {
|
||||||
|
/// Whether the high bit of the package type byte is set.
|
||||||
|
is_encrypted: bool,
|
||||||
|
|
||||||
|
/// Number of plaintext payload bytes to write after removing encryption and
|
||||||
|
/// any trailing block padding.
|
||||||
|
filesize: usize,
|
||||||
|
|
||||||
|
/// Per-package AES-CBC initialization vector.
|
||||||
|
iv: [u8; 16],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unpack a complete `.sig` file held in memory.
|
||||||
|
///
|
||||||
|
/// The input must include both the 512-byte header and the wrapped payload. The
|
||||||
|
/// returned bytes are suitable to write directly as the stripped output package,
|
||||||
|
/// such as a `.swu`, `.zip`, or `.json` file.
|
||||||
|
///
|
||||||
|
/// For encrypted packages, this function decrypts the bytes after the header
|
||||||
|
/// with AES-256-CBC and then truncates the plaintext to the `filesize` recorded
|
||||||
|
/// in the header. For unencrypted packages, it simply copies exactly `filesize`
|
||||||
|
/// bytes from the post-header payload.
|
||||||
|
pub fn unpack_sig(raw: &[u8]) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
|
if raw.len() < HEADER_LEN {
|
||||||
|
return Err("input is too small to contain a .sig header".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let header = parse_header(&raw[..HEADER_LEN])?;
|
||||||
|
let payload = &raw[HEADER_LEN..];
|
||||||
|
|
||||||
|
if header.is_encrypted {
|
||||||
|
decrypt_payload(payload, &header)
|
||||||
|
} else {
|
||||||
|
strip_plain_payload(payload, header.filesize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse the fixed-width `.sig` header.
|
||||||
|
///
|
||||||
|
/// Offsets are based on the browser implementation:
|
||||||
|
///
|
||||||
|
/// - `0x00..0x04`: big-endian `ELEG` magic.
|
||||||
|
/// - `0x04`: package type; bit `0x80` indicates encryption.
|
||||||
|
/// - `0x08..0x10`: little-endian plaintext payload size.
|
||||||
|
/// - `0xA0..0xB0`: AES-CBC IV.
|
||||||
|
fn parse_header(header: &[u8]) -> Result<Header, Box<dyn Error>> {
|
||||||
|
if header.len() < HEADER_LEN {
|
||||||
|
return Err("header must be at least 512 bytes".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let magic = u32::from_be_bytes(header[0..4].try_into()?);
|
||||||
|
if magic != MAGIC {
|
||||||
|
return Err(format!("invalid .sig magic: 0x{magic:08x}").into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let package_type = header[0x04];
|
||||||
|
let is_encrypted = package_type & 0x80 != 0;
|
||||||
|
let filesize = u64::from_le_bytes(header[0x08..0x10].try_into()?)
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| "payload filesize does not fit on this platform")?;
|
||||||
|
let iv = header[0xA0..0xB0].try_into()?;
|
||||||
|
|
||||||
|
Ok(Header {
|
||||||
|
is_encrypted,
|
||||||
|
filesize,
|
||||||
|
iv,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the plaintext payload for packages that are marked unencrypted.
|
||||||
|
///
|
||||||
|
/// Even unencrypted packages still include the `.sig` header. The header's
|
||||||
|
/// `filesize` field defines how many bytes after the header are part of the
|
||||||
|
/// actual package payload.
|
||||||
|
fn strip_plain_payload(payload: &[u8], filesize: usize) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
|
if filesize > payload.len() {
|
||||||
|
return Err(format!(
|
||||||
|
"header payload size {} exceeds payload size {}",
|
||||||
|
filesize,
|
||||||
|
payload.len()
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(payload[..filesize].to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decrypt and trim an encrypted `.sig` payload.
|
||||||
|
///
|
||||||
|
/// The browser version appends an extra encrypted PKCS#7 padding block to work
|
||||||
|
/// around Web Crypto's required padding behavior. Rust's `cbc` crate can decrypt
|
||||||
|
/// without padding, so this implementation decrypts the payload as-is and then
|
||||||
|
/// trims to the plaintext length declared in the header.
|
||||||
|
fn decrypt_payload(payload: &[u8], header: &Header) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
|
if payload.len() % 16 != 0 {
|
||||||
|
return Err("encrypted payload length is not a multiple of the AES block size".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buffer = payload.to_vec();
|
||||||
|
let decrypted = Aes256CbcDec::new(&AES_KEY.into(), &header.iv.into())
|
||||||
|
.decrypt_padded_mut::<NoPadding>(&mut buffer)
|
||||||
|
.map_err(|_| "AES-CBC decryption failed")?;
|
||||||
|
|
||||||
|
if header.filesize > decrypted.len() {
|
||||||
|
return Err(format!(
|
||||||
|
"header payload size {} exceeds decrypted payload size {}",
|
||||||
|
header.filesize,
|
||||||
|
decrypted.len()
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(decrypted[..header.filesize].to_vec())
|
||||||
|
}
|
||||||
+60
-2
@@ -1,3 +1,61 @@
|
|||||||
fn main() {
|
//! Command-line entry point for the `sig` utility.
|
||||||
println!("Hello, world!");
|
//!
|
||||||
|
//! The binary intentionally keeps very little logic here. Clap-owned command
|
||||||
|
//! definitions live in [`cli`], while the Centauri Carbon 2 `.sig` format and
|
||||||
|
//! cryptographic details live in [`crypto`]. That split keeps argument parsing
|
||||||
|
//! separate from file-format handling so future commands can reuse the same
|
||||||
|
//! unpacking code without duplicating CLI concerns.
|
||||||
|
|
||||||
|
use std::{error::Error, fs, path::PathBuf};
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
mod cli;
|
||||||
|
mod crypto;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
use cli::{Cli, Command};
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
|
||||||
|
match cli.command {
|
||||||
|
Command::Strip(args) => strip(args)?,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute the `strip` subcommand.
|
||||||
|
///
|
||||||
|
/// `strip` removes the 512-byte `.sig` wrapper and writes the contained package
|
||||||
|
/// bytes. For encrypted packages, [`crypto::unpack_sig`] performs the AES-CBC
|
||||||
|
/// decryption before returning the payload.
|
||||||
|
fn strip(args: cli::StripArgs) -> Result<(), Box<dyn Error>> {
|
||||||
|
let raw = fs::read(&args.input)?;
|
||||||
|
let unpacked = crypto::unpack_sig(&raw)?;
|
||||||
|
let output_path = args
|
||||||
|
.output
|
||||||
|
.unwrap_or_else(|| default_output_path(&args.input));
|
||||||
|
|
||||||
|
fs::write(&output_path, unpacked)?;
|
||||||
|
println!("wrote {}", output_path.display());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Derive the default output path used by the web unpacker.
|
||||||
|
///
|
||||||
|
/// A conventional input like `firmware.zip.sig` becomes `firmware.zip`. If the
|
||||||
|
/// input does not use the `.sig` extension, the tool still writes beside the
|
||||||
|
/// original file but changes the extension to `.decrypted` to avoid overwriting
|
||||||
|
/// the input by accident.
|
||||||
|
fn default_output_path(input: &std::path::Path) -> PathBuf {
|
||||||
|
if input.extension().is_some_and(|ext| ext == "sig") {
|
||||||
|
input.with_extension("")
|
||||||
|
} else {
|
||||||
|
input.with_extension("decrypted")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
//! Tests for the Carbon 2 `.sig` unpacking path.
|
||||||
|
|
||||||
|
/// Encrypted `ota-package-list.json.sig` fixture bytes.
|
||||||
|
///
|
||||||
|
/// The bytes are embedded directly so the test does not depend on external test
|
||||||
|
/// fixture files being present at runtime.
|
||||||
|
const ENCRYPTED_OTA_PACKAGE_LIST: &[u8] = &[
|
||||||
|
0x45, 0x4c, 0x45, 0x47, 0x83, 0x01, 0x02, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x6f, 0x74, 0x61, 0x2d, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x2d, 0x6c, 0x69, 0x73, 0x74,
|
||||||
|
0x2e, 0x6a, 0x73, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x90, 0x8e, 0x89, 0x12, 0x45, 0xea, 0x37, 0x14, 0xad, 0x5b, 0x8e, 0x5f, 0xa9, 0xcf, 0xbb, 0xc9,
|
||||||
|
0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xbf, 0x17, 0x4e, 0xde, 0xae, 0x8d, 0x1e, 0x00, 0xe2, 0xdd, 0x44, 0x99, 0x42, 0x32, 0x8e, 0x1a,
|
||||||
|
0x79, 0xba, 0x2e, 0x50, 0x3d, 0x16, 0x28, 0xaa, 0x50, 0xf8, 0x2d, 0xaf, 0x35, 0x4d, 0x1c, 0xec,
|
||||||
|
0x5d, 0xe0, 0x8d, 0x4c, 0x80, 0x44, 0xda, 0xc7, 0x63, 0x5c, 0xa7, 0x0f, 0x99, 0xe3, 0x81, 0xb8,
|
||||||
|
0x27, 0xef, 0xd2, 0x6f, 0xc0, 0x49, 0x91, 0x52, 0xa1, 0x0c, 0x32, 0xea, 0xc1, 0x5c, 0xb8, 0xd1,
|
||||||
|
0xb8, 0xe4, 0x65, 0x97, 0x4e, 0x4f, 0x2c, 0x7d, 0x8c, 0xb4, 0xf2, 0x65, 0x92, 0xbc, 0x90, 0x97,
|
||||||
|
0x6d, 0xc1, 0xc1, 0x13, 0xa0, 0xf4, 0xd1, 0xe4, 0xfe, 0x28, 0x77, 0xbc, 0xfe, 0x1f, 0x90, 0x52,
|
||||||
|
0x72, 0x03, 0x56, 0xc0, 0xce, 0xbb, 0x63, 0x0d, 0xf2, 0x52, 0x81, 0x52, 0xaa, 0xb9, 0xc3, 0x59,
|
||||||
|
0xf2, 0x58, 0xa8, 0xb6, 0xb1, 0x12, 0xcd, 0x5d, 0x99, 0x11, 0x85, 0x80, 0x2e, 0xa7, 0x1d, 0x84,
|
||||||
|
0xff, 0x94, 0x4f, 0xc7, 0x94, 0x14, 0xe2, 0x48, 0xb8, 0x50, 0xc8, 0xff, 0xec, 0x23, 0xf3, 0xdb,
|
||||||
|
0x11, 0x9c, 0xca, 0xc3, 0x6a, 0xd1, 0x42, 0xbc, 0x5b, 0x4b, 0x8e, 0xcf, 0xb3, 0x74, 0x1f, 0x34,
|
||||||
|
0x80, 0xad, 0xa6, 0x39, 0x98, 0x15, 0x30, 0x31, 0xca, 0x21, 0x03, 0x67, 0x69, 0x50, 0x04, 0xd9,
|
||||||
|
0xc5, 0x4a, 0xea, 0xd7, 0xed, 0xdc, 0x32, 0xa3, 0x33, 0xc8, 0x87, 0x21, 0xf8, 0x21, 0x96, 0xad,
|
||||||
|
0xf8, 0xf5, 0xb4, 0xe1, 0xe9, 0xc3, 0x72, 0x2f, 0x88, 0x03, 0xa8, 0x89, 0x65, 0x39, 0xb6, 0x53,
|
||||||
|
0x98, 0xef, 0xcb, 0x5a, 0x54, 0x13, 0x4c, 0x4a, 0x55, 0xe9, 0x91, 0x90, 0xcb, 0x39, 0xf0, 0x00,
|
||||||
|
0xee, 0x31, 0xed, 0xa4, 0x43, 0x06, 0x93, 0x75, 0x38, 0x6a, 0xe3, 0xe0, 0x54, 0x8f, 0x0a, 0x3a,
|
||||||
|
0xf3, 0x79, 0x21, 0x9f, 0x41, 0x97, 0xac, 0x80, 0x8b, 0x87, 0xd0, 0x44, 0x11, 0x04, 0xc6, 0x76,
|
||||||
|
0x82, 0x5a, 0x35, 0x77, 0x8f, 0x15, 0x05, 0x05, 0x55, 0xfb, 0xeb, 0x46, 0x1a, 0xe8, 0xb8, 0x73,
|
||||||
|
0x0d, 0xa7, 0xd8, 0xd1, 0x18, 0x3e, 0x46, 0x24, 0x86, 0x67, 0xc3, 0x0c, 0xe5, 0x7d, 0x56, 0x77,
|
||||||
|
0x3b, 0xd3, 0xd6, 0x95, 0x15, 0x65, 0xf5, 0xb8, 0x68, 0x8d, 0xc9, 0x0b, 0x16, 0x45, 0x91, 0xef,
|
||||||
|
0xcc, 0xe5, 0x54, 0x16, 0x9b, 0x8c, 0x2c, 0x76, 0x4b, 0xa2, 0x44, 0xbc, 0x49, 0xda, 0x33, 0x04,
|
||||||
|
0xc5, 0xb6, 0xd7, 0xc6, 0x3b, 0x49, 0x95, 0x08, 0x8c, 0xd3, 0x7e, 0x2b, 0x65, 0xe6, 0x83, 0x62,
|
||||||
|
0x81, 0xd3, 0x11, 0x24, 0x5a, 0xac, 0x9b, 0xd3, 0xcd, 0x19, 0x06, 0xde, 0xf3, 0x0a, 0xb4, 0x5f,
|
||||||
|
0x18, 0xc6, 0x06, 0x60, 0x67, 0xca, 0x0c, 0x36, 0x34, 0xb8, 0x92, 0x69, 0xde, 0xf6, 0xe7, 0x64,
|
||||||
|
0x58, 0x80, 0x4f, 0x5c, 0xf5, 0x44, 0x62, 0x21, 0x2c, 0x9d, 0xe4, 0x18, 0x71, 0xba, 0x18, 0x43,
|
||||||
|
0x0f, 0x44, 0xa8, 0x1b, 0x35, 0x22, 0x36, 0x77, 0x0c, 0xc6, 0x22, 0x46, 0x4c, 0x11, 0xec, 0xfd,
|
||||||
|
0xb9, 0xef, 0x51, 0xb5, 0xb2, 0x8e, 0x3f, 0x2f, 0xa5, 0x7c, 0x3c, 0x46, 0x57, 0xfd, 0x5c, 0x8c,
|
||||||
|
0x6f, 0xc4, 0x13, 0x99, 0xf0, 0x06, 0x01, 0x5f, 0x92, 0x51, 0xc6, 0x94, 0x29, 0xf5, 0xb4, 0xd0,
|
||||||
|
0x5e, 0x2f, 0x1e, 0x0a, 0x93, 0x8d, 0x08, 0x00, 0xcd, 0x71, 0x4d, 0x6c, 0x11, 0xad, 0xe3, 0x78,
|
||||||
|
0xba, 0xd3, 0x6d, 0xe8, 0xf5, 0x93, 0xd6, 0x83, 0x94, 0x9d, 0xf1, 0xf0, 0xde, 0x04, 0xcc, 0x41,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Expected plaintext `ota-package-list.json` fixture bytes.
|
||||||
|
const EXPECTED_OTA_PACKAGE_LIST: &[u8] = &[
|
||||||
|
0x7b, 0x22, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x20, 0x5b, 0x7b, 0x22,
|
||||||
|
0x66, 0x69, 0x6c, 0x65, 0x22, 0x3a, 0x20, 0x22, 0x63, 0x63, 0x32, 0x5f, 0x65, 0x65, 0x62, 0x30,
|
||||||
|
0x30, 0x31, 0x5f, 0x30, 0x31, 0x2e, 0x30, 0x33, 0x2e, 0x30, 0x32, 0x2e, 0x33, 0x36, 0x5f, 0x32,
|
||||||
|
0x30, 0x32, 0x36, 0x30, 0x33, 0x32, 0x36, 0x31, 0x37, 0x31, 0x37, 0x34, 0x35, 0x2e, 0x73, 0x77,
|
||||||
|
0x75, 0x2e, 0x73, 0x69, 0x67, 0x22, 0x2c, 0x20, 0x22, 0x68, 0x61, 0x73, 0x68, 0x22, 0x3a, 0x20,
|
||||||
|
0x22, 0x34, 0x36, 0x61, 0x61, 0x34, 0x35, 0x39, 0x62, 0x36, 0x31, 0x33, 0x34, 0x30, 0x61, 0x38,
|
||||||
|
0x34, 0x66, 0x66, 0x35, 0x32, 0x65, 0x32, 0x38, 0x36, 0x35, 0x33, 0x34, 0x37, 0x64, 0x34, 0x32,
|
||||||
|
0x61, 0x35, 0x36, 0x38, 0x33, 0x35, 0x31, 0x39, 0x31, 0x39, 0x34, 0x34, 0x31, 0x66, 0x62, 0x39,
|
||||||
|
0x64, 0x62, 0x36, 0x65, 0x31, 0x38, 0x35, 0x34, 0x32, 0x34, 0x32, 0x32, 0x30, 0x62, 0x31, 0x39,
|
||||||
|
0x32, 0x22, 0x7d, 0x5d, 0x2c, 0x20, 0x22, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3a,
|
||||||
|
0x20, 0x22, 0x30, 0x31, 0x2e, 0x30, 0x33, 0x2e, 0x30, 0x32, 0x2e, 0x33, 0x36, 0x22, 0x7d,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Decrypting the signed OTA package list should reproduce the known plaintext
|
||||||
|
/// JSON fixture byte-for-byte.
|
||||||
|
#[test]
|
||||||
|
fn decrypts_ota_package_list_fixture() {
|
||||||
|
let actual = super::crypto::unpack_sig(ENCRYPTED_OTA_PACKAGE_LIST)
|
||||||
|
.expect("failed to unpack embedded .sig fixture");
|
||||||
|
|
||||||
|
assert_eq!(actual, EXPECTED_OTA_PACKAGE_LIST);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user