[−][src]Struct gbl::Gbl
In-memory representation of a GBL file.
Typestate
This struct makes heavy use of typestate to track whether the GBL is
encrypted or contains an ECDSA signature: The E
type parameter can be
any of Encrypted
, NotEncrypted
or MaybeEncrypted
to indicate
whether the program data in the GBL is encrypted, while S
can be any of
Signed
, NotSigned
or MaybeSigned
to indicate the presence of a
signature.
Typestate is used to make misuse of the APIs in this crate as difficult as possible. It rules out many possibly unwanted operations statically, such as:
- Encrypting an already-encrypted GBL.
- Decrypting a GBL that is not encrypted.
- Signing a GBL that already has a signature.
- Adding a
ProgramData
section to an encrypted GBL (only encrypted section are allowed there) or to a signed GBL (which would invalidate the signature). - Accessing the plain-text data sections using
data_sections()
on an encrypted GBL.
Attempting to perform any of those operations will make the program fail
compilation. The only operations that perform runtime checking are the
into_encrypted/signed
methods mentioned above.
The downside of such a typestate-based API is that it is rather cumbersome and complex. However, correctness was deemed more important here (in fact, we've already had internal API-misuse accidents that would've been prevented by the typestate-based API).
Maybe
The Maybe*
typestates indicate that there is no compile-time knowledge of
the state. They are present when parsing an external GBL file and provide
their information only at runtime.
On Gbl
objects containing Maybe*
, only a few general methods are
available. To get access to the methods that require more precise typestate,
into_encrypted
, into_not_encrypted
, into_signed
, or
into_not_signed
can be called to effectively downcast from MaybeX
to
X
or NotX
.
Examples
A simple example that shows how to deal with Maybe*
typestate:
use gbl::Gbl; use gbl::marker::*; // This GBL is neither signed nor encrypted let raw_bytes: &[u8] = include_bytes!("../test-data/empty/empty.gbl"); let gbl: Gbl<MaybeEncrypted, MaybeSigned> = Gbl::parse(raw_bytes)?; match gbl.into_not_encrypted() { // `into_not_encrypted` returns a `Gbl<NotEncrypted, _>` in the success case Ok(not_encrypted) => { // Let's write out the type we get in the `Ok` branch: let not_encrypted: Gbl<NotEncrypted, MaybeSigned> = not_encrypted; // Getting a `NotEncrypted` GBL just made the `data_sections()` accessor available: not_encrypted.data_sections(); // In almost all cases, you want to get rid of the `MaybeSigned` as well. // Let's just unwrap that one to keep it simple: let gbl: Gbl<NotEncrypted, NotSigned> = not_encrypted.into_not_signed().unwrap(); // (you can remove the `MaybeEncrypted` and `MaybeSigned` in any order) // Now that we have a `Gbl<NotEncrypted, NotSigned>`, a lot of useful methods // just became available: `push_data_section`, `encrypt`, `sign`, ... // Refer to the API documentation for more details on methods and their availability. } Err(encrypted) => { // The GBL *is* encrypted. We won't handle that case here and leave it as an // exercise to the reader. unimplemented!("GBL is encrypted"); } }
Methods
impl<'a> Gbl<MaybeEncrypted<'a>, MaybeSigned<'a>>
[src]
pub fn parse<T: AsRef<[u8]> + ?Sized>(bytes: &'a T) -> Result<Self, Error>
[src]
Parses a GBL file from raw bytes.
The resulting Gbl
will be MaybeEncrypted
and MaybeSigned
, because
those states can't be statically determined when parsing an existing
GBL. You can use into_encrypted
, into_signed
, etc. to downcast
to a more specific typestate that has more methods available.
Parsing is protected against malicious GBL files that specify very large sizes or contain an abnormal number of tags to cause a DoS via memory exhaustion. Note that this protection does not extend to the user code that reads the GBL file into memory.
impl<'a> Gbl<NotEncrypted<'a>, NotSigned<'a>>
[src]
Methods that only work on non-encrypted and non-signed GBLs.
pub fn from_app_image(image: AppImage<'a>) -> Self
[src]
Creates a Gbl
object from a raw application image.
The resulting GBL file will contain the AppInfo
from the given image
as well as a single ProgramData
section writing the raw image data
to the device flash.
pub fn from_parts(app_info: AppInfo, data: ProgramData<'a>) -> Self
[src]
Creates a new GBL file from an existing AppInfo
structure and a
ProgramData
section.
Additional program data sections can be added by calling
push_data_section
.
pub fn push_data_section(&mut self, section: ProgramData<'a>)
[src]
Appends a ProgramData
section to the data section list.
It is the user's responsibility to ensure that no sections overlap (or reference invalid addresses).
Also see data_sections
for read-only access to all ProgramData
sections.
pub fn encrypt(self, key: AesKey) -> Gbl<Encrypted<'a>, NotSigned<'a>>
[src]
Encrypts the content of this GBL file using an AES-128 key.
This will generate a random 12-Byte nonce using the operating system's random number generator. The nonce will be used for encryption and decryption and is stored inside the encryption header inside the encrypted GBL.
Note that the validity of the key cannot be checked: Passing an invalid key will result in encryption succeeding, but resulting in garbage data, which will then fail to parse properly.
Method availability
This method is only available when self
is NotEncrypted
and
NotSigned
. It turns a NotEncrypted
and NotSigned
GBL into an
Encrypted
and NotSigned
GBL.
impl<'a, S> Gbl<MaybeEncrypted<'a>, S> where
S: SignatureState<'a>,
[src]
S: SignatureState<'a>,
MaybeEncrypted
-> (Not)Encrypted
downcasting methods.
pub fn into_encrypted(
self
) -> Result<Gbl<Encrypted<'a>, S>, Gbl<NotEncrypted<'a>, S>>
[src]
self
) -> Result<Gbl<Encrypted<'a>, S>, Gbl<NotEncrypted<'a>, S>>
Inspects the MaybeEncrypted
, downcasting self
to a
Gbl<Encrypted, _>
if it is encrypted.
Otherwise, downcasts self
to a Gbl<NotEncrypted, _>
. This means that
the Maybe*
gets stripped in either case.
Note that this will never actually modify or drop data in self
. All
that changes is the type, potentially making more methods available on
the returned Gbl
.
This will not modify the signature typestate S
. It works with any S
and simply passes it through.
pub fn into_not_encrypted(
self
) -> Result<Gbl<NotEncrypted<'a>, S>, Gbl<Encrypted<'a>, S>>
[src]
self
) -> Result<Gbl<NotEncrypted<'a>, S>, Gbl<Encrypted<'a>, S>>
Inspects the MaybeEncrypted
, downcasting self
to a
Gbl<NotEncrypted, _>
if it is not encrypted.
Otherwise, downcasts self
to a Gbl<Encrypted, _>
. This means that
the Maybe*
gets stripped in either case.
Note that this will never actually modify or drop data in self
. All
that changes is the type, potentially making more methods available on
the returned Gbl
.
This will not modify the signature typestate S
. It works with any S
and simply passes it through.
impl<'a, E, S> Gbl<E, S> where
E: EncryptionState<'a>,
S: SignatureState<'a>,
[src]
E: EncryptionState<'a>,
S: SignatureState<'a>,
Generic upcasting methods to Maybe*
variants.
pub fn into_maybe_encrypted(self) -> Gbl<MaybeEncrypted<'a>, S>
[src]
Erases the encryption type state by upcasting to MaybeEncrypted
.
This can be used to avoid code duplication in code that needs to handle both encrypted and non-encrypted GBLs.
This conversion can also be performed using Into
/From
.
pub fn into_maybe_signed(self) -> Gbl<E, MaybeSigned<'a>>
[src]
Erases the signature type state by upcasting to MaybeSigned
.
This can be used to avoid code duplication in code that needs to handle both signed and non-signed GBLs.
This conversion can also be performed using Into
/From
.
impl<'a, E> Gbl<E, MaybeSigned<'a>> where
E: EncryptionState<'a>,
[src]
E: EncryptionState<'a>,
MaybeSigned
-> (Not)Signed
downcasting methods.
pub fn into_signed(self) -> Result<Gbl<E, Signed<'a>>, Gbl<E, NotSigned<'a>>>
[src]
Inspects the MaybeSigned
, downcasting self
to a Gbl<_, Signed>
if
it is signed.
Otherwise, downcasts self
to a Gbl<_, NotSigned>
. This means that
the Maybe*
gets stripped in either case.
Note that this will never actually modify or drop data in self
. All
that changes is the type, potentially making more methods available on
the returned Gbl
.
This will not modify the encryption typestate E
. It works with any E
and simply passes it through.
pub fn into_not_signed(
self
) -> Result<Gbl<E, NotSigned<'a>>, Gbl<E, Signed<'a>>>
[src]
self
) -> Result<Gbl<E, NotSigned<'a>>, Gbl<E, Signed<'a>>>
Inspects the MaybeSigned
, downcasting self
to a Gbl<_, NotSigned>
if it is signed.
Otherwise, downcasts self
to a Gbl<_, Signed>
. This means that the
Maybe*
gets stripped in either case.
Note that this will never actually modify or drop data in self
. All
that changes is the type, potentially making more methods available on
the returned Gbl
.
This will not modify the encryption typestate E
. It works with any E
and simply passes it through.
impl<'a, S> Gbl<NotEncrypted<'a>, S> where
S: SignatureState<'a>,
[src]
S: SignatureState<'a>,
Methods available only on non-encrypted GBLs. Signature may or may not be present.
pub fn data_sections(&self) -> &[ProgramData<'a>]
[src]
Returns the data sections to be programmed to the device's flash memory.
This method is only available if self
is NotEncrypted
, because an
encrypted GBL does not allow reading the data sections.
Also see push_data_section
.
impl<'a, E> Gbl<E, Signed<'a>> where
E: EncryptionState<'a>,
[src]
E: EncryptionState<'a>,
pub fn verify_signature(&self, pubkey: &P256PublicKey) -> Result<(), Error>
[src]
Attempts to verify the ECDSA signature attached to the GBL.
If the signature was not created by the private key belonging to
pubkey
, an error will be returned.
If the GBL is encrypted, the signature is computed over the encrypted data. Consequently, decrypting the GBL disposes of the signature. Check the signature before decrypting!
Parameters
pubkey
: The public P-256 key to verify against.
pub fn remove_signature(self) -> Gbl<E, NotSigned<'a>>
[src]
Strips the signature from self
without verifying it.
This converts a Signed
GBL into a NotSigned
one, enabling methods
that require the GBL to have no signature (such as push_data_section
and encrypt
).
impl<'a, E> Gbl<E, NotSigned<'a>> where
E: EncryptionState<'a>,
[src]
E: EncryptionState<'a>,
pub fn sign(self, key: &P256KeyPair) -> Result<Gbl<E, Signed<'a>>, Error>
[src]
Creates and appends a digital signature for self
using a private EC
key.
Returns the signed GBL.
Note that the signature created by this method is attached to the
GBL container, not the contained application image. In other words,
this signature can not be checked by the bootloader during secure
boot. If you want to use secure boot, you need to sign the application
image itself by using AppImage::sign
. Also be aware that flashing
an application image with an invalid signature will prevent the device
from rebooting back into it, so you likely want to have both a signed
application image and a signed GBL container you can check before
flashing.
Parameters
key
: The P-256 keypair to sign with.
impl<'a> Gbl<Encrypted<'a>, NotSigned<'a>>
[src]
pub fn decrypt(
self,
key: AesKey
) -> Result<Gbl<NotEncrypted<'a>, NotSigned<'a>>, Error>
[src]
self,
key: AesKey
) -> Result<Gbl<NotEncrypted<'a>, NotSigned<'a>>, Error>
Decrypts the content of this GBL file using a raw AES-128 key.
The decrypted data will be parsed into GBL tags and returned as a new
Gbl
object based on self
.
Note that the validity of the key cannot be checked: Passing an invalid key will result in decryption succeeding, but results in garbage data, which will then fail to parse properly.
impl<'a, E, S> Gbl<E, S> where
E: EncryptionState<'a>,
S: SignatureState<'a>,
[src]
E: EncryptionState<'a>,
S: SignatureState<'a>,
General methods that work on GBLs regardless of encryption/signing state.
pub fn into_owned(self) -> Gbl<E::StaticSelf, S::StaticSelf>
[src]
Takes ownership of any borrowed data in self
.
This will heap-allocate owned storage for all data in self
and copy
the data there.
pub fn clone(&'a self) -> Self
[src]
A cheap-ish, lifetime-restricted version of Clone
.
The returned object will share as much data as possible with self
, but
cannot outlive it. You can call .into_owned()
on the returned object
to make it own all of its data.
pub fn to_bytes(&self) -> Vec<u8>
[src]
Converts the GBL file to its binary representation.
pub fn write<W: Write>(&self, writer: W) -> Result<()>
[src]
Serializes the binary representation of this GBL into a writer.
pub fn is_signed(&self) -> bool
[src]
Returns whether self
contains a digital signature.
Call verify_signature
to check if the signature belongs to a known key
pair.
pub fn is_encrypted(&self) -> bool
[src]
Returns whether self
contains encrypted program data.
If this is the case, you must call decrypt
to get access to the
contained data.
Trait Implementations
impl<'a, S: SignatureState<'a>> From<Gbl<Encrypted<'a>, S>> for Gbl<MaybeEncrypted<'a>, S>
[src]
impl<'a, S: SignatureState<'a>> From<Gbl<NotEncrypted<'a>, S>> for Gbl<MaybeEncrypted<'a>, S>
[src]
fn from(gbl: Gbl<NotEncrypted<'a>, S>) -> Self
[src]
impl<'a, E: EncryptionState<'a>> From<Gbl<E, Signed<'a>>> for Gbl<E, MaybeSigned<'a>>
[src]
impl<'a, E: EncryptionState<'a>> From<Gbl<E, NotSigned<'a>>> for Gbl<E, MaybeSigned<'a>>
[src]
impl<E: Debug, S: Debug> Debug for Gbl<E, S>
[src]
Auto Trait Implementations
impl<E, S> Sync for Gbl<E, S> where
E: Sync,
S: Sync,
E: Sync,
S: Sync,
impl<E, S> Send for Gbl<E, S> where
E: Send,
S: Send,
E: Send,
S: Send,
impl<E, S> Unpin for Gbl<E, S> where
E: Unpin,
S: Unpin,
E: Unpin,
S: Unpin,
impl<E, S> RefUnwindSafe for Gbl<E, S> where
E: RefUnwindSafe,
S: RefUnwindSafe,
E: RefUnwindSafe,
S: RefUnwindSafe,
impl<E, S> UnwindSafe for Gbl<E, S> where
E: UnwindSafe,
S: UnwindSafe,
E: UnwindSafe,
S: UnwindSafe,
Blanket Implementations
impl<T> From<T> for T
[src]
impl<T, U> Into<U> for T where
U: From<T>,
[src]
U: From<T>,
impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]
U: Into<T>,
type Error = Infallible
The type returned in the event of a conversion error.
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]
impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]
U: TryFrom<T>,
type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]
impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]
T: ?Sized,
fn borrow_mut(&mut self) -> &mut T
[src]
impl<T> Borrow<T> for T where
T: ?Sized,
[src]
T: ?Sized,
impl<T> Any for T where
T: 'static + ?Sized,
[src]
T: 'static + ?Sized,