#![no_std]
#![warn(clippy::large_futures)]
use alloc::boxed::Box;
use core::fmt::{self, Debug, Display};
use core::marker::PhantomData;
use embedded_io_async::{Error, ErrorType, Read, ReadExactError, Write};
use log::{error, info};
use num_bigint::{traits::ModInverse, ToBigUint};
use num_traits::cast::ToPrimitive;
use rand_core::{CryptoRng, RngCore};
use rsa::{traits::PublicKeyParts, BigUint, Pss, RsaPrivateKey, RsaPublicKey};
use sha2::{Digest, Sha256};
extern crate alloc;
#[cfg(feature = "std")]
pub use std::*;
pub mod rsa {
pub use ::rsa::*;
}
pub struct NullWrite<E>(PhantomData<fn() -> E>);
impl<E> Default for NullWrite<E> {
fn default() -> Self {
Self::new()
}
}
impl<E> NullWrite<E> {
pub const fn new() -> Self {
Self(PhantomData::<fn() -> E>)
}
}
impl<E> ErrorType for NullWrite<E>
where
E: Error,
{
type Error = E;
}
impl<E> Write for NullWrite<E>
where
E: Error,
{
async fn write(&mut self, data: &[u8]) -> Result<usize, Self::Error> {
Ok(data.len())
}
}
#[derive(Debug)]
pub enum SignError<E> {
Io(E),
InvalidImageLen,
}
impl<E> SignError<E> {
pub fn map<E2>(self, f: impl FnOnce(E) -> E2) -> SignError<E2> {
match self {
SignError::Io(e) => SignError::Io(f(e)),
SignError::InvalidImageLen => SignError::InvalidImageLen,
}
}
}
impl<E> Display for SignError<E>
where
E: Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Io(e) => write!(f, "IO error: {:?}", e),
Self::InvalidImageLen => write!(f, "Invalid image length"),
}
}
}
impl<E> core::error::Error for SignError<E> where E: Debug {}
#[derive(Debug)]
pub enum VerifyError<E> {
Io(E),
Eof,
InvalidSignatureBlock,
InvalidHash,
InvalidSignature,
InvalidImageLen,
}
impl<E> VerifyError<E> {
pub fn map<E2>(self, f: impl FnOnce(E) -> E2) -> VerifyError<E2> {
match self {
VerifyError::Io(e) => VerifyError::Io(f(e)),
VerifyError::Eof => VerifyError::Eof,
VerifyError::InvalidSignatureBlock => VerifyError::InvalidSignatureBlock,
VerifyError::InvalidHash => VerifyError::InvalidHash,
VerifyError::InvalidSignature => VerifyError::InvalidSignature,
VerifyError::InvalidImageLen => VerifyError::InvalidImageLen,
}
}
}
impl<E> From<ReadExactError<E>> for VerifyError<E> {
fn from(e: ReadExactError<E>) -> Self {
match e {
ReadExactError::UnexpectedEof => Self::Eof,
ReadExactError::Other(e) => Self::Io(e),
}
}
}
impl<E> Display for VerifyError<E>
where
E: Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Io(e) => write!(f, "IO error: {:?}", e),
Self::Eof => write!(f, "Unexpected EOF"),
Self::InvalidSignatureBlock => write!(f, "Invalid signature block"),
Self::InvalidHash => write!(f, "Invalid hash"),
Self::InvalidSignature => write!(f, "Invalid signature"),
Self::InvalidImageLen => write!(f, "Invalid image length"),
}
}
}
impl<E> core::error::Error for VerifyError<E> where E: Debug {}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ImageType {
Bootloader,
App,
}
impl ImageType {
pub fn align(&self) -> usize {
match self {
ImageType::Bootloader => 4096,
ImageType::App => 65536,
}
}
}
#[repr(C)]
#[repr(packed)]
pub struct SBV2RsaPubKey {
rsa_public_modulus: [u8; 384],
rsa_public_exponent: [u8; 4],
rsa_precalc_r: [u8; 384],
rsa_precalc_m: [u8; 4],
}
impl SBV2RsaPubKey {
pub fn create(pub_key: &RsaPublicKey) -> Self {
let mut this = Self::new_empty();
this.fill(pub_key);
this
}
pub async fn save_hash<W>(&self, mut out: W) -> Result<(), W::Error>
where
W: Write,
{
let mut hasher = Sha256::new();
self.fill_hash(&mut hasher);
out.write_all(&hasher.finalize()).await?;
Ok(())
}
async fn load<R>(&mut self, mut read: R) -> Result<(), ReadExactError<R::Error>>
where
R: Read,
{
read.read_exact(&mut self.rsa_public_modulus).await?;
read.read_exact(&mut self.rsa_public_exponent).await?;
read.read_exact(&mut self.rsa_precalc_r).await?;
read.read_exact(&mut self.rsa_precalc_m).await?;
Ok(())
}
async fn save<W>(&self, mut out: W) -> Result<(), W::Error>
where
W: Write,
{
out.write_all(&self.rsa_public_modulus).await?;
out.write_all(&self.rsa_public_exponent).await?;
out.write_all(&self.rsa_precalc_r).await?;
out.write_all(&self.rsa_precalc_m).await?;
Ok(())
}
fn pub_key(&self) -> RsaPublicKey {
RsaPublicKey::new(
BigUint::from_bytes_le(&self.rsa_public_modulus),
BigUint::from_bytes_le(&self.rsa_public_exponent),
)
.unwrap()
}
fn fill_hash(&self, hasher: &mut Sha256) {
hasher.update(self.rsa_public_modulus);
hasher.update(self.rsa_public_exponent);
hasher.update(self.rsa_precalc_r);
hasher.update(self.rsa_precalc_m);
}
const fn new_empty() -> Self {
Self {
rsa_public_modulus: [0; 384],
rsa_public_exponent: [0; 4],
rsa_precalc_r: [0; 384],
rsa_precalc_m: [0; 4],
}
}
fn fill(&mut self, pub_key: &RsaPublicKey) {
let m = -pub_key
.n()
.mod_inverse(1.to_biguint().unwrap() << 32)
.unwrap();
let rr = 1.to_biguint().unwrap() << (pub_key.n().bits() * 2);
let rinv = rr % pub_key.n();
self.rsa_public_modulus
.copy_from_slice(&pub_key.n().to_bytes_le());
self.rsa_public_exponent
.copy_from_slice(&pub_key.e().to_i32().unwrap().to_le_bytes());
self.rsa_precalc_r.copy_from_slice(&rinv.to_bytes_le());
self.rsa_precalc_m
.copy_from_slice(&((m.to_i64().unwrap() & 0xffffffff) as i32).to_le_bytes());
}
}
#[repr(C)]
#[repr(packed)]
pub struct SBV2RsaSignatureBlock {
magic: u8,
version: u8,
padding: [u8; 2],
sha256: [u8; 32],
rsa_pub_key: SBV2RsaPubKey,
rsa_pss_signature: [u8; 384],
crc32: [u8; 4],
padding2: [u8; 16],
}
impl SBV2RsaSignatureBlock {
const PAGE_SIZE: usize = 4096;
const MAGIC_BYTE: u8 = 0xe7;
const VERSION: u8 = 2;
pub async fn sign<C, R, W>(
priv_key: &RsaPrivateKey,
rng: &mut C,
buf: &mut [u8],
image: R,
image_type: ImageType,
mut out: W,
) -> Result<Self, SignError<R::Error>>
where
C: RngCore + CryptoRng,
R: Read,
W: Write,
W::Error: Into<R::Error>,
{
let block = Self::create(priv_key, rng, buf, image, image_type, Some(&mut out)).await?;
block
.save(out, Some(buf))
.await
.map_err(Into::<R::Error>::into)
.map_err(SignError::Io)?;
Ok(block)
}
pub async fn load_and_verify<R>(
buf: &mut [u8],
mut image: R,
image_type: ImageType,
) -> Result<&Self, VerifyError<R::Error>>
where
R: Read,
{
if buf.len() <= Self::PAGE_SIZE {
panic!("Buffer too small; should be > {}B", Self::PAGE_SIZE);
}
let mut size = 0;
let mut offset = 0;
let mut hasher = Sha256::new();
loop {
let read = image.read(&mut buf[offset..]).await.unwrap();
offset += read;
size += read;
if buf.len() == offset || read == 0 {
hasher.update(&buf[..offset - Self::PAGE_SIZE]);
buf.copy_within(offset - Self::PAGE_SIZE.., 0);
offset = Self::PAGE_SIZE;
}
if read == 0 {
break;
}
}
if offset != Self::PAGE_SIZE || (size - Self::PAGE_SIZE) % image_type.align() != 0 {
Err(VerifyError::InvalidImageLen)?;
}
let block = unsafe { (buf.as_ptr() as *const Self).as_ref() }.unwrap();
let digest = hasher.finalize();
block.verify_image_hash(digest.as_ref())?;
Ok(block)
}
pub async fn create<C, R, W>(
priv_key: &RsaPrivateKey,
rng: &mut C,
buf: &mut [u8],
image: R,
image_type: ImageType,
out: Option<W>,
) -> Result<Self, SignError<R::Error>>
where
C: RngCore + CryptoRng,
R: Read,
W: Write,
W::Error: Into<R::Error>,
{
let mut block = Self::new_empty();
block
.fill(priv_key, rng, buf, image, out, image_type)
.await?;
Ok(block)
}
pub async fn load<R>(&mut self, mut read: R) -> Result<(), VerifyError<R::Error>>
where
R: Read,
{
self.clear();
let mut bt = [0; 2];
read.read_exact(&mut bt).await?;
if bt[0] != Self::MAGIC_BYTE || bt[1] != Self::VERSION {
Err(VerifyError::InvalidSignatureBlock)?;
}
self.magic = bt[0];
self.version = bt[1];
read.read_exact(&mut self.padding).await?;
read.read_exact(&mut self.sha256).await?;
self.rsa_pub_key.load(&mut read).await?;
read.read_exact(&mut self.rsa_pss_signature).await?;
read.read_exact(&mut self.crc32).await?;
read.read_exact(&mut self.padding2).await?;
if self.crc32 != self.crc32() {
Err(VerifyError::InvalidSignatureBlock)?;
}
Ok(())
}
pub async fn save<W>(&self, mut out: W, padded: Option<&mut [u8]>) -> Result<(), W::Error>
where
W: Write,
{
out.write_all(&[self.magic]).await?;
out.write_all(&[self.version]).await?;
out.write_all(&self.padding).await?;
out.write_all(&self.sha256).await?;
self.rsa_pub_key.save(&mut out).await?;
out.write_all(&self.rsa_pss_signature).await?;
out.write_all(&self.crc32).await?;
out.write_all(&self.padding2).await?;
if let Some(buf) = padded {
let mut remainder = Self::PAGE_SIZE - core::mem::size_of_val(self);
if remainder != 0 {
buf.fill(0xff);
while remainder > 0 {
let len = core::cmp::min(remainder, buf.len());
out.write_all(&buf[..len]).await?;
remainder -= len;
}
}
}
Ok(())
}
pub async fn save_pubkey_hash<W>(&self, out: W) -> Result<(), W::Error>
where
W: Write,
{
self.rsa_pub_key.save_hash(out).await
}
pub async fn verify<R>(
&self,
buf: &mut [u8],
image: R,
image_type: ImageType,
) -> Result<(), VerifyError<R::Error>>
where
R: Read,
{
let mut hasher = Sha256::new();
let aligned = Self::read_write_hash(
&mut hasher,
buf,
image,
Option::<NullWrite<R::Error>>::None,
image_type,
false,
)
.await
.map_err(VerifyError::Io)?;
if !aligned {
Err(VerifyError::InvalidImageLen)?;
}
let digest = hasher.finalize();
let hashed = digest.as_ref();
self.verify_image_hash(hashed)?;
Ok(())
}
pub fn verify_image_hash<E>(&self, hash: &[u8]) -> Result<(), VerifyError<E>> {
if hash != self.sha256 {
Err(VerifyError::InvalidHash)?;
}
let pub_key = self.rsa_pub_key.pub_key();
let pss = Pss::new_with_salt::<Sha256>(32);
let mut signature = [0; 384];
signature.copy_from_slice(&self.rsa_pss_signature);
signature.reverse();
pub_key
.verify(pss, hash, &signature)
.map_err(|_| VerifyError::InvalidSignature)?;
Ok(())
}
const fn new_empty() -> Self {
Self {
magic: Self::MAGIC_BYTE,
version: Self::VERSION,
padding: [0; 2],
sha256: [0; 32],
rsa_pub_key: SBV2RsaPubKey::new_empty(),
rsa_pss_signature: [0; 384],
crc32: [0; 4],
padding2: [0; 16],
}
}
async fn fill<C, R, W>(
&mut self,
priv_key: &RsaPrivateKey,
rng: &mut C,
buf: &mut [u8],
image: R,
out: Option<W>,
image_type: ImageType,
) -> Result<(), SignError<R::Error>>
where
C: RngCore + CryptoRng,
R: Read,
W: Write,
W::Error: Into<R::Error>,
{
self.clear();
self.fill_pub_key(&priv_key.to_public_key());
self.fill_hash(buf, image, out, image_type, true).await?;
self.fill_signature(rng, priv_key);
self.fill_crc32();
Ok(())
}
fn clear(&mut self) {
*self = Self::new_empty();
}
fn fill_pub_key(&mut self, pub_key: &RsaPublicKey) {
self.rsa_pub_key.fill(pub_key);
}
fn fill_signature<C: RngCore + CryptoRng>(&mut self, rng: &mut C, priv_key: &RsaPrivateKey) {
let pss = Pss::new_with_salt::<Sha256>(32);
let signature: Box<[u8]> = priv_key
.sign_with_rng(rng, pss, &self.sha256)
.unwrap()
.into();
self.rsa_pss_signature.copy_from_slice(signature.as_ref());
self.rsa_pss_signature.reverse(); }
async fn fill_hash<R, W>(
&mut self,
buf: &mut [u8],
image: R,
out: Option<W>,
image_type: ImageType,
pad: bool,
) -> Result<(), SignError<R::Error>>
where
R: Read,
W: Write,
W::Error: Into<R::Error>,
{
let mut hasher = Sha256::new();
if !Self::read_write_hash(&mut hasher, buf, image, out, image_type, pad)
.await
.map_err(SignError::Io)?
{
Err(SignError::InvalidImageLen)?; }
self.sha256.copy_from_slice(hasher.finalize().as_ref());
Ok(())
}
fn fill_crc32(&mut self) {
let crc32 = self.crc32();
self.crc32.copy_from_slice(&crc32);
}
fn crc32(&self) -> [u8; 4] {
const CRC32: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
let mut crc = CRC32.digest();
crc.update(&[self.magic]);
crc.update(&[self.version]);
crc.update(&self.padding);
crc.update(&self.sha256);
crc.update(&self.rsa_pub_key.rsa_public_modulus);
crc.update(&self.rsa_pub_key.rsa_public_exponent);
crc.update(&self.rsa_pub_key.rsa_precalc_r);
crc.update(&self.rsa_pub_key.rsa_precalc_m);
crc.update(&self.rsa_pss_signature);
let checksum = crc.finalize();
checksum.to_le_bytes()
}
async fn read_write_hash<R, W>(
hasher: &mut Sha256,
buf: &mut [u8],
mut image: R,
mut out: Option<W>,
image_type: ImageType,
pad: bool,
) -> Result<bool, R::Error>
where
R: Read,
W: Write,
W::Error: Into<R::Error>,
{
let mut size: usize = 0;
loop {
let read = image.read(buf).await?;
if read == 0 {
break;
}
Self::write_hash(hasher, &buf[..read], out.as_mut())
.await
.map_err(Into::into)?;
size += read;
}
let align = image_type.align();
let mut remainder = align - size % align;
if remainder != align {
if pad {
if matches!(image_type, ImageType::App) {
error!("App image size ({size}B) is not a multiple of {align}B. Padding requested, but can't pad App images.");
Ok(false)
} else {
info!("Bootloader image size ({size}B) is not a multiple of {align}B. Padding {remainder}B with 0xFF");
buf.fill(0xff);
while remainder > 0 {
let to_write = remainder.min(buf.len());
Self::write_hash(hasher, &buf[..to_write], out.as_mut())
.await
.map_err(Into::into)?;
remainder -= to_write;
}
Ok(true)
}
} else {
Ok(false)
}
} else {
Ok(true)
}
}
async fn write_hash<W>(
hasher: &mut Sha256,
data: &[u8],
mut out: Option<W>,
) -> Result<(), W::Error>
where
W: Write,
{
if let Some(write) = out.as_mut() {
write.write_all(data).await.map_err(Into::into)?;
}
hasher.update(data);
Ok(())
}
}
#[cfg(feature = "std")]
mod std {
use std::io;
use embedded_io_async::{ErrorType, Read, Write};
extern crate std;
pub struct AsyncIo<T>(T);
impl<T> AsyncIo<T> {
pub const fn new(inner: T) -> Self {
Self(inner)
}
}
impl<T> ErrorType for AsyncIo<T> {
type Error = io::Error;
}
impl<T> Read for AsyncIo<T>
where
T: io::Read,
{
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
self.0.read(buf)
}
}
impl<T> Write for AsyncIo<T>
where
T: io::Write,
{
async fn write(&mut self, data: &[u8]) -> Result<usize, Self::Error> {
self.0.write(data)
}
}
}
#[cfg(all(test, feature = "pks"))]
mod test {
use core::convert::Infallible;
use alloc::vec::Vec;
use embedded_io_async::{ErrorType, Write};
use super::ImageType;
use rand_core::{CryptoRng, RngCore};
use rsa::pkcs8::DecodePrivateKey;
extern crate alloc;
static PRIV_KEY: &str = r#"
-----BEGIN PRIVATE KEY-----
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
HLi+/wQ5736LzHUphwOfBDZZ
-----END PRIVATE KEY-----
"#;
static IMAGE: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
struct Rng(u8);
impl RngCore for Rng {
fn next_u32(&mut self) -> u32 {
let mut result = [0; 4];
self.fill_bytes(&mut result);
u32::from_le_bytes(result)
}
fn next_u64(&mut self) -> u64 {
let mut result = [0; 8];
self.fill_bytes(&mut result);
u64::from_le_bytes(result)
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
for i in dest {
*i = self.0;
self.0 += 1;
}
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
self.fill_bytes(dest);
Ok(())
}
}
impl CryptoRng for Rng {}
struct AsyncIo<T>(T);
impl<T> ErrorType for AsyncIo<T> {
type Error = Infallible;
}
impl Write for AsyncIo<&mut Vec<u8>> {
async fn write(&mut self, data: &[u8]) -> Result<usize, Self::Error> {
self.0.extend_from_slice(data);
Ok(data.len())
}
}
#[test]
fn test() {
let priv_key = super::rsa::RsaPrivateKey::from_pkcs8_pem(PRIV_KEY).unwrap();
let mut buf = [0; 5000];
let mut out = Vec::new();
let mut sha = Vec::new();
let mut rng = Rng(0);
embassy_futures::block_on(async {
let signature = super::SBV2RsaSignatureBlock::sign(
&priv_key,
&mut rng,
&mut buf,
IMAGE,
ImageType::Bootloader,
AsyncIo(&mut out),
)
.await
.unwrap();
signature.save_pubkey_hash(AsyncIo(&mut sha)).await.unwrap();
assert_eq!(
&sha,
&[
66, 75, 181, 110, 45, 200, 254, 51, 193, 128, 186, 133, 116, 47, 246, 223, 131,
205, 201, 187, 29, 16, 233, 45, 4, 113, 121, 145, 220, 211, 91, 77
]
);
super::SBV2RsaSignatureBlock::load_and_verify(
&mut buf,
&mut out.as_ref(),
ImageType::Bootloader,
)
.await
.unwrap();
});
}
}