#![doc(html_root_url = "https://docs.rs/gbl/0.3.1")]
#[macro_use]
extern crate num_derive;
#[macro_use]
extern crate log;
#[macro_use]
extern crate failure;
pub extern crate uuid;
mod appimage;
mod crypto;
mod error;
mod key;
pub mod marker;
mod utils;
pub use crate::appimage::{AppImage, AppInfo};
pub use crate::error::{Error, ErrorKind};
pub use crate::key::{AesKey, P256KeyPair, P256PublicKey};
use crate::marker::private::*;
use crate::marker::*;
use crate::utils::{Blob, Crc32Writer};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use either::Either;
use num_traits::FromPrimitive;
use std::borrow::Cow;
use std::io;
use std::io::prelude::*;
use std::u32;
const TAG_SIZE_LIMIT: u32 = 10 * 1024 * 1024;
const TAG_COUNT_LIMIT: u32 = 16;
#[derive(Debug)]
pub struct Gbl<E, S> {
enc: E,
sig: S,
}
impl<'a> Gbl<MaybeEncrypted<'a>, MaybeSigned<'a>> {
pub fn parse<T: AsRef<[u8]> + ?Sized>(bytes: &'a T) -> Result<Self, Error> {
Self::parse_impl(bytes.as_ref())
}
fn parse_impl(mut bytes: &'a [u8]) -> Result<Self, Error> {
if bytes.len() < 4 {
return Err(Error::parse_err("GBL file too small to be valid"));
}
let mut w = crc32fast::Hasher::new();
w.update(&bytes[..bytes.len() - 4]);
let checksum_computed = w.finalize();
let reader = &mut bytes;
let mut tag_count = 0;
let mut header = None;
let mut app_info = None;
let mut sections = Vec::new(); let mut signature = None;
let mut enc_header = None;
let mut enc_sections = Vec::new(); let checksum;
loop {
tag_count += 1;
if tag_count > TAG_COUNT_LIMIT {
return Err(Error::parse_err(format!(
"exceeded the tag count limit of {} during \
parsing",
TAG_COUNT_LIMIT
)));
}
match Tag::parse(reader)? {
Tag::Header(hdr) => {
if header.is_some() {
return Err(Error::parse_err("duplicate header"));
}
header = Some(hdr);
}
Tag::End(c) => {
if reader.is_empty() {
checksum = c;
break;
} else {
return Err(Error::parse_err("trailing data after end tag"));
}
}
Tag::EncryptionHeader(header) => {
if enc_header.is_some() {
return Err(Error::parse_err("duplicate encryption header"));
}
enc_header = Some(header);
}
Tag::Signature(sig) => {
if signature.is_some() {
return Err(Error::parse_err("duplicate signature"));
}
signature = Some(sig);
}
Tag::AppInfo(info) => {
if app_info.is_some() {
return Err(Error::parse_err("duplicate appinfo section"));
}
app_info = Some(info);
}
Tag::ProgramData(data) => sections.push(data),
Tag::EncryptedData(data) => enc_sections.push(Blob(data)),
}
}
let header = header.ok_or_else(|| Error::parse_err("invalid GBL: no header found"))?;
if checksum != checksum_computed && !cfg!(fuzzing) {
return Err(Error::parse_err(format!(
"invalid CRC checksum: got {:#010X}, expected {:#010X}",
checksum, checksum_computed
)));
}
if header.signed != signature.is_some() {
return Err(Error::parse_err(format!(
"header sign bit: {}; signature present: {}",
header.signed,
signature.is_some()
)));
}
if header.encrypted != enc_header.is_some() {
return Err(Error::parse_err(format!(
"header encryption bit: {}; encryption header present: {}",
header.encrypted,
enc_header.is_some()
)));
}
if header.encrypted && !sections.is_empty() {
return Err(Error::parse_err(
"unencrypted program data sections present, but header claims \
that encryption is used",
));
}
if !header.encrypted && !enc_sections.is_empty() {
return Err(Error::parse_err(
"encrypted program data sections present, but header claims \
that encryption isn't used",
));
}
if let Some(enc) = &enc_header {
let actual_bytes = enc_sections.iter().map(|sec| sec.len()).sum();
if enc.total_bytes as usize != actual_bytes {
return Err(Error::parse_err(format!(
"encryption header specifies {} encrypted bytes, but total is {}",
enc.total_bytes, actual_bytes
)));
}
}
Ok(Gbl {
enc: MaybeEncrypted {
inner: if header.encrypted {
Either::Left(Encrypted {
enc_header: enc_header.unwrap(),
enc_sections,
})
} else {
Either::Right(NotEncrypted {
app_info: app_info.unwrap(),
sections,
})
},
},
sig: MaybeSigned {
inner: if let Some(signature) = signature {
Either::Left(Signed { signature })
} else {
Either::Right(NotSigned::new())
},
},
})
}
}
impl<'a> Gbl<NotEncrypted<'a>, NotSigned<'a>> {
pub fn from_app_image(image: AppImage<'a>) -> Self {
Gbl {
enc: NotEncrypted {
app_info: *image.app_info(),
sections: vec![ProgramData {
flash_addr: 0,
data: Blob(image.into_raw()),
}],
},
sig: NotSigned::new(),
}
}
pub fn from_parts(app_info: AppInfo, data: ProgramData<'a>) -> Self {
Self {
enc: NotEncrypted {
app_info,
sections: vec![data],
},
sig: NotSigned::new(),
}
}
pub fn push_data_section(&mut self, section: ProgramData<'a>) {
self.enc.sections.push(section);
}
pub fn encrypt(self, key: AesKey) -> Gbl<Encrypted<'a>, NotSigned<'a>> {
debug_assert!(!self.is_encrypted());
let nonce = crypto::random_nonce();
let sec_count = self.enc.sections.len() + 1; let mut sections: Vec<Vec<u8>> = Vec::with_capacity(sec_count);
{
let mut buf: Vec<u8> = Vec::new();
Tag::AppInfo(self.enc.app_info)
.write(&mut buf)
.expect("failed to write to `Vec`");
sections.push(buf);
}
for sec in &self.enc.sections {
let mut buf: Vec<u8> = Vec::new();
Tag::ProgramData(sec.clone())
.write(&mut buf)
.expect("failed to write to `Vec`");
sections.push(buf);
}
let encrypted = crypto::crypt(key, nonce, §ions);
info!("parsing {} sections", encrypted.len());
let mut total_bytes: u32 = 0;
let mut enc_sections = Vec::new();
for sec in encrypted {
total_bytes += sec.len() as u32;
enc_sections.push(Blob(Cow::from(sec)));
}
Gbl {
enc: Encrypted {
enc_header: EncryptionHeader {
total_bytes,
nonce: Blob(nonce),
},
enc_sections,
},
sig: self.sig,
}
}
}
impl<'a, S> Gbl<MaybeEncrypted<'a>, S>
where
S: SignatureState<'a>,
{
pub fn into_encrypted(self) -> Result<Gbl<Encrypted<'a>, S>, Gbl<NotEncrypted<'a>, S>> {
match self.enc.inner {
Either::Left(enc) => Ok(Gbl { enc, sig: self.sig }),
Either::Right(not_enc) => Err(Gbl {
enc: not_enc,
sig: self.sig,
}),
}
}
pub fn into_not_encrypted(self) -> Result<Gbl<NotEncrypted<'a>, S>, Gbl<Encrypted<'a>, S>> {
match self.enc.inner {
Either::Left(enc) => Err(Gbl { enc, sig: self.sig }),
Either::Right(not_enc) => Ok(Gbl {
enc: not_enc,
sig: self.sig,
}),
}
}
}
impl<'a, E, S> Gbl<E, S>
where
E: EncryptionState<'a>,
S: SignatureState<'a>,
{
pub fn into_maybe_encrypted(self) -> Gbl<MaybeEncrypted<'a>, S> {
Gbl {
enc: MaybeEncrypted {
inner: self.enc.into_either(),
},
sig: self.sig,
}
}
pub fn into_maybe_signed(self) -> Gbl<E, MaybeSigned<'a>> {
Gbl {
enc: self.enc,
sig: MaybeSigned {
inner: self.sig.into_either(),
},
}
}
}
impl<'a, S: SignatureState<'a>> From<Gbl<Encrypted<'a>, S>> for Gbl<MaybeEncrypted<'a>, S> {
fn from(gbl: Gbl<Encrypted<'a>, S>) -> Self {
gbl.into_maybe_encrypted()
}
}
impl<'a, S: SignatureState<'a>> From<Gbl<NotEncrypted<'a>, S>> for Gbl<MaybeEncrypted<'a>, S> {
fn from(gbl: Gbl<NotEncrypted<'a>, S>) -> Self {
gbl.into_maybe_encrypted()
}
}
impl<'a, E: EncryptionState<'a>> From<Gbl<E, Signed<'a>>> for Gbl<E, MaybeSigned<'a>> {
fn from(gbl: Gbl<E, Signed<'a>>) -> Gbl<E, MaybeSigned<'a>> {
gbl.into_maybe_signed()
}
}
impl<'a, E: EncryptionState<'a>> From<Gbl<E, NotSigned<'a>>> for Gbl<E, MaybeSigned<'a>> {
fn from(gbl: Gbl<E, NotSigned<'a>>) -> Gbl<E, MaybeSigned<'a>> {
gbl.into_maybe_signed()
}
}
impl<'a, E> Gbl<E, MaybeSigned<'a>>
where
E: EncryptionState<'a>,
{
pub fn into_signed(self) -> Result<Gbl<E, Signed<'a>>, Gbl<E, NotSigned<'a>>> {
match self.sig.inner {
Either::Left(signed) => Ok(Gbl {
enc: self.enc,
sig: signed,
}),
Either::Right(not_signed) => Err(Gbl {
enc: self.enc,
sig: not_signed,
}),
}
}
pub fn into_not_signed(self) -> Result<Gbl<E, NotSigned<'a>>, Gbl<E, Signed<'a>>> {
match self.sig.inner {
Either::Left(signed) => Err(Gbl {
enc: self.enc,
sig: signed,
}),
Either::Right(not_signed) => Ok(Gbl {
enc: self.enc,
sig: not_signed,
}),
}
}
}
impl<'a, S> Gbl<NotEncrypted<'a>, S>
where
S: SignatureState<'a>,
{
pub fn data_sections(&self) -> &[ProgramData<'a>] {
&self.enc.sections
}
}
impl<'a, E> Gbl<E, Signed<'a>>
where
E: EncryptionState<'a>,
{
pub fn verify_signature(&self, pubkey: &P256PublicKey) -> Result<(), Error> {
let signature = &self.sig.signature;
let mut signed_data = Vec::new();
self.write_data_to_sign(&mut signed_data)
.expect("writing into `Vec` failed");
let mut raw_signature = [0; 64];
raw_signature.copy_from_slice(&signature.raw);
crypto::verify_signature(pubkey, &raw_signature, &signed_data)
}
pub fn remove_signature(self) -> Gbl<E, NotSigned<'a>> {
Gbl {
enc: self.enc,
sig: NotSigned::new(),
}
}
}
impl<'a, E> Gbl<E, NotSigned<'a>>
where
E: EncryptionState<'a>,
{
pub fn sign(self, key: &P256KeyPair) -> Result<Gbl<E, Signed<'a>>, Error> {
let mut sign_data = Vec::with_capacity(1024); self.write_data_to_sign(&mut sign_data)
.expect("writing into `Vec` failed");
let sig = crypto::create_signature(key, &sign_data)?;
Ok(Gbl {
enc: self.enc,
sig: Signed {
signature: Signature {
raw: Blob(sig.to_vec().into()),
},
},
})
}
}
impl<'a> Gbl<Encrypted<'a>, NotSigned<'a>> {
pub fn decrypt(self, key: AesKey) -> Result<Gbl<NotEncrypted<'a>, NotSigned<'a>>, Error> {
let enc_header = &self.enc.enc_header;
let nonce = enc_header.nonce.0;
let decrypted = crypto::crypt(key, nonce, &self.enc.enc_sections);
info!("parsing {} decrypted sections", decrypted.len());
let mut sections = Vec::with_capacity(self.enc.enc_sections.len() - 1);
let mut appinfo = None;
for section in decrypted {
let section = Blob(section);
debug!("decrypted data: {:?}", section);
let reader: &mut &[u8] = &mut &*section;
let tag = Tag::parse(reader)?;
if !reader.is_empty() {
return Err(Error::parse_err(format!(
"trailing bytes in encrypted section of {} bytes",
section.len()
)));
}
match tag {
Tag::AppInfo(info) => {
if appinfo.is_some() {
return Err(Error::parse_err("duplicate appinfo section"));
}
appinfo = Some(info);
}
Tag::ProgramData(data) => sections.push(data.into_owned()),
_ => {
return Err(Error::parse_err(format!(
"decrypted tag {:?} is invalid in an encrypted section",
tag.tag_id()
)));
}
}
}
Ok(Gbl {
enc: NotEncrypted {
app_info: appinfo.unwrap(),
sections,
},
sig: NotSigned::new(),
})
}
}
impl<'a, E, S> Gbl<E, S>
where
E: EncryptionState<'a>,
S: SignatureState<'a>,
{
pub fn into_owned(self) -> Gbl<E::StaticSelf, S::StaticSelf> {
Gbl {
enc: self.enc.into_owned(),
sig: self.sig.into_owned(),
}
}
pub fn clone(&'a self) -> Self {
Self {
enc: self.enc.clone(),
sig: self.sig.clone(),
}
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut buf = Vec::new();
self.write(&mut buf).expect("writing into `Vec<u8>` failed");
buf
}
pub fn write<W: Write>(&self, writer: W) -> io::Result<()> {
let mut writer = Crc32Writer::new(writer);
self.to_tags(|tag| tag.write(&mut writer))?;
writer.write_u32::<LittleEndian>(TagId::End as u32)?;
writer.write_u32::<LittleEndian>(4)?; let crc = writer.digest.finalize();
writer.inner.write_u32::<LittleEndian>(crc)?;
Ok(())
}
fn to_tags<F, Er>(&self, mut f: F) -> Result<(), Er>
where
F: FnMut(&Tag) -> Result<(), Er>,
{
f(&Tag::Header(Header {
signed: self.is_signed(),
encrypted: self.is_encrypted(),
}))?;
match self.enc.as_either_ref() {
Either::Left(enc) => {
f(&Tag::EncryptionHeader(enc.enc_header))?;
for section in &enc.enc_sections {
f(&Tag::EncryptedData(Cow::Borrowed(§ion.0)))?;
}
}
Either::Right(not_enc) => {
f(&Tag::AppInfo(not_enc.app_info))?;
for programdata in ¬_enc.sections {
f(&Tag::ProgramData(programdata.clone()))?;
}
}
}
if let Either::Left(sig) = self.sig.as_either_ref() {
f(&Tag::Signature(sig.signature.clone()))?;
}
Ok(())
}
pub fn is_signed(&self) -> bool {
self.sig.as_either_ref().is_left()
}
pub fn is_encrypted(&self) -> bool {
self.enc.as_either_ref().is_left()
}
fn write_data_to_sign<W: Write>(&self, mut w: W) -> io::Result<()> {
self.to_tags(|tag| match tag {
Tag::Header(h) => Tag::Header(Header {
signed: true,
encrypted: h.encrypted,
})
.write(&mut w), Tag::Signature(_) => Ok(()), other => other.write(&mut w),
})
}
}
enum Tag<'a> {
Header(Header),
AppInfo(AppInfo),
ProgramData(ProgramData<'a>),
Signature(Signature<'a>),
End(u32),
EncryptionHeader(EncryptionHeader),
EncryptedData(Cow<'a, [u8]>),
}
impl<'a> Tag<'a> {
fn parse(reader: &mut &'a [u8]) -> Result<Self, Error> {
let tag_id = reader.read_u32::<LittleEndian>()?;
let tag_len = reader.read_u32::<LittleEndian>()?;
if tag_len > TAG_SIZE_LIMIT {
return Err(Error::parse_err(format!(
"tag {:04X} exceeds size limit of {} bytes (length = {} bytes)",
tag_id, TAG_SIZE_LIMIT, tag_len
)));
}
let mut tag_buf = reader.get(..tag_len as usize).ok_or_else(|| {
Error::parse_err(format!("tag length {} exceeds total file length", tag_len))
})?;
*reader = &reader[tag_len as usize..];
let tag_id = TagId::from_u32(tag_id)
.ok_or_else(|| Error::parse_err(format!("invalid GBL tag {:010X}", tag_id)))?;
debug!("tag {:?}, {} bytes", tag_id, tag_len);
match tag_id {
TagId::Header => Ok(Tag::Header(Header::parse(&tag_buf)?)),
TagId::End => {
if tag_len != 4 {
return Err(Error::parse_err(format!(
"invalid end tag length: expected 4 bytes, got {}",
tag_len
)));
}
let checksum = tag_buf.read_u32::<LittleEndian>()?;
info!("checksum: {:#010X}", checksum);
Ok(Tag::End(checksum))
}
TagId::AppInfo => {
debug!("raw appinfo: {:?}", Blob(tag_buf));
Ok(Tag::AppInfo(AppInfo::parse(&tag_buf)?))
}
TagId::EncryptionInitHeader => {
debug!("raw encryption header: {:?}", Blob(tag_buf));
if tag_len != 4 + 12 {
return Err(Error::parse_err(format!(
"unexpected length of encryption init header: got {}, expected 16",
tag_len
)));
}
let total_enc_bytes = tag_buf.read_u32::<LittleEndian>()?;
let mut nonce = [0; 12];
tag_buf.read_exact(&mut nonce)?;
let parsed = EncryptionHeader {
total_bytes: total_enc_bytes,
nonce: nonce.into(),
};
debug!("encryption header: {:?}", parsed);
Ok(Tag::EncryptionHeader(parsed))
}
TagId::ProgramData | TagId::ProgramData2 => {
let data = if tag_len > 64 {
&tag_buf[..64]
} else {
&tag_buf
};
debug!("raw program data: {:?}", Blob(data));
let flash_addr = tag_buf.read_u32::<LittleEndian>()?;
Ok(Tag::ProgramData(ProgramData {
flash_addr,
data: Blob(tag_buf.into()),
}))
}
TagId::EncryptedProgramData => Ok(Tag::EncryptedData(tag_buf.into())),
TagId::Signature => {
debug!("raw signature: {:?}", Blob(tag_buf));
if tag_len != 64 {
return Err(Error::parse_err(format!(
"signature is {} Bytes, expected 64",
tag_len
)));
}
Ok(Tag::Signature(Signature {
raw: Blob(tag_buf.into()),
}))
}
TagId::Bootloader | TagId::Metadata => {
if cfg!(fuzzing) {
Err(Error::parse_err("NYI: bootloader/metadata tag"))
} else {
unimplemented!()
}
}
}
}
fn tag_id(&self) -> TagId {
match self {
Tag::Header(_) => TagId::Header,
Tag::AppInfo(_) => TagId::AppInfo,
Tag::ProgramData(_) => TagId::ProgramData2,
Tag::Signature(_) => TagId::Signature,
Tag::End(_) => TagId::End,
Tag::EncryptionHeader(_) => TagId::EncryptionInitHeader,
Tag::EncryptedData(_) => TagId::EncryptedProgramData,
}
}
fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
let mut buf = Vec::new();
self.write_raw(&mut buf)
.expect("writing into a Vec<u8> failed");
writer.write_u32::<LittleEndian>(self.tag_id() as u32)?;
writer.write_u32::<LittleEndian>(buf.len() as u32)?;
writer.write_all(&buf)?;
Ok(())
}
fn write_raw<W: Write>(&self, writer: &mut W) -> io::Result<()> {
match self {
Tag::Header(hdr) => {
writer.write_u32::<LittleEndian>(0x03000000)?;
writer.write_u8(if hdr.encrypted { 1 } else { 0 })?;
writer.write_u8(if hdr.signed { 1 } else { 0 })?;
writer.write_u8(0)?;
writer.write_u8(0)?;
}
Tag::AppInfo(appinfo) => {
appinfo.write(writer)?;
}
Tag::ProgramData(data) => {
writer.write_u32::<LittleEndian>(data.flash_addr)?;
writer.write_all(&data.data)?;
}
Tag::Signature(sig) => {
writer.write_all(&sig.raw)?;
}
Tag::End(crc) => {
writer.write_u32::<LittleEndian>(*crc)?;
}
Tag::EncryptionHeader(hdr) => {
writer.write_u32::<LittleEndian>(hdr.total_bytes)?;
writer.write_all(&hdr.nonce)?;
}
Tag::EncryptedData(data) => {
writer.write_all(data)?;
}
}
Ok(())
}
}
#[derive(FromPrimitive, Debug)]
#[repr(u32)]
enum TagId {
Header = 0x03A617EB,
AppInfo = 0xF40A0AF4,
Bootloader = 0xF50909F5,
ProgramData = 0xFE0101FE,
ProgramData2 = 0xFD0303FD, Metadata = 0xF60808F6,
Signature = 0xF70A0AF7,
End = 0xFC0404FC,
EncryptionInitHeader = 0xFA0606FA,
EncryptedProgramData = 0xF90707F9,
}
#[derive(Debug, Copy, Clone)]
struct Header {
signed: bool,
encrypted: bool,
}
impl Header {
fn parse(mut bytes: &[u8]) -> Result<Self, Error> {
debug!("raw header: {:?}", bytes);
if bytes.len() != 8 {
return Err(Error::parse_err(format!(
"got {} header bytes, expected 8",
bytes.len()
)));
}
let mut fixed = [0u8; 4];
bytes.read_exact(&mut fixed)?;
if fixed != [0, 0, 0, 3] {
return Err(Error::parse_err(format!(
"invalid or unknown header format: {:?}",
fixed
)));
}
let encrypted = match bytes.read_u8()? {
0 => false,
1 => true,
invalid => {
return Err(Error::parse_err(format!(
"invalid value for encryption byte: {:#04X} (expected 0 or 1)",
invalid
)));
}
};
let signed = match bytes.read_u8()? {
0 => false,
1 => true,
invalid => {
return Err(Error::parse_err(format!(
"invalid value for sign byte: {:#04X} (expected 0 or 1)",
invalid
)));
}
};
let zero = bytes.read_u16::<LittleEndian>()?;
if zero != 0 {
return Err(Error::parse_err(format!(
"invalid trailing word in header (expected 0, got {:#06X})",
zero
)));
}
Ok(Header { signed, encrypted })
}
}
#[derive(Debug)]
pub struct ProgramData<'a> {
flash_addr: u32,
data: Blob<Cow<'a, [u8]>>,
}
impl<'a> ProgramData<'a> {
pub fn new<D>(addr: u32, data: D) -> Self
where
D: Into<Cow<'a, [u8]>>,
{
Self {
flash_addr: addr,
data: Blob(data.into()),
}
}
pub fn start_addr(&self) -> u32 {
self.flash_addr
}
pub fn bytes(&self) -> &[u8] {
&self.data
}
fn into_owned(self) -> ProgramData<'static> {
ProgramData {
flash_addr: self.flash_addr,
data: Blob(self.data.0.into_owned().into()),
}
}
fn clone(&'a self) -> Self {
ProgramData {
flash_addr: self.flash_addr,
data: Blob(Cow::Borrowed(&self.data.0)),
}
}
}
impl<'a> From<AppImage<'a>> for ProgramData<'a> {
fn from(image: AppImage<'a>) -> Self {
Self {
flash_addr: 0,
data: Blob(image.into_raw()),
}
}
}
#[derive(Debug)]
struct Signature<'a> {
raw: Blob<Cow<'a, [u8]>>, }
impl<'a> Signature<'a> {
fn into_owned(self) -> Signature<'static> {
Signature {
raw: Blob(self.raw.0.into_owned().into()),
}
}
fn clone(&'a self) -> Self {
Signature {
raw: Blob(Cow::Borrowed(&self.raw.0)),
}
}
}
#[derive(Debug, Copy, Clone)]
struct EncryptionHeader {
total_bytes: u32,
nonce: Blob<[u8; 12]>,
}