Skip to content

Commit

Permalink
[fuzzing] Implementing initial support for fuzzing LMS (#618)
Browse files Browse the repository at this point in the history
* treewide: Refactor SHA256 driver into an implemented trait

`Sha256HardwareDriver` is now the primary implementation of
`Sha256`. Where a concrete implementation is required,
`Sha256HardwareDriver` is almost always the correct instance.

This refactor makes it easier to test consumers of this driver, where
software implementations of SHA256 may be preferred. Follow-up commits
will provide a software implementation, which will then be used for
fuzz testing the LMS driver.

Signed-off-by: Benjamin Doron <[email protected]>

* [fuzzing] Implement initial support for LMS driver fuzzing

Instructions for running the supported fuzzers can be found at
https://gist.github.com/benjamindoron/59313b863b079f6f5af56af83d32648e.
This can be migrated to official documentation.

Signed-off-by: Benjamin Doron <[email protected]>

* Make Sha256 driver refactor as minimal as possible.

Signed-off-by: Benjamin Doron <[email protected]>
Co-authored-by: Kor Nielsen <[email protected]>
  • Loading branch information
benjamindoron and korran authored Sep 13, 2023
1 parent 00b0503 commit 8d1f779
Show file tree
Hide file tree
Showing 12 changed files with 230 additions and 18 deletions.
5 changes: 5 additions & 0 deletions drivers/fuzz/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
target
corpus
artifacts
coverage
*.log
48 changes: 48 additions & 0 deletions drivers/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Licensed under the Apache-2.0 license

[package]
name = "caliptra-drivers-fuzz"
version = "0.0.0"
publish = false
edition = "2021"

[package.metadata]
cargo-fuzz = true

[dependencies]
libfuzzer-sys = { version = "0.4.6", optional = true }
afl = { version = "0.13.3", optional = true }
zerocopy = "0.6.1"
arbitrary = { version = "1.3.0", optional = true, features = ["derive"] }
sha2 = { version = "0.10.2", default-features = false, features = ["compress"] }

[patch.crates-io]
byteorder = { git = "https://github.com/benjamindoron/byteorder.git", branch = "struct_aware-1.4.3" }
zerocopy = { git = "https://github.com/benjamindoron/zerocopy.git", branch = "struct_aware-v0.6.3" }

[dependencies.caliptra-drivers]
path = ".."

[dependencies.caliptra-image-types]
path = "../../image/types"
features = ["arbitrary"]

[dependencies.caliptra-lms-types]
path = "../../lms-types"
features = ["arbitrary"]

[features]
struct-aware = ["arbitrary"]

# Prevent this from interfering with workspaces
[workspace]
members = ["."]

[profile.release]
debug = 1

[[bin]]
name = "fuzz_target_lms"
path = "src/fuzz_target_lms.rs"
test = false
doc = false
Binary file added drivers/fuzz/common_corpus/lms_24_test01.dat
Binary file not shown.
Binary file added drivers/fuzz/common_corpus/lms_kat.dat
Binary file not shown.
91 changes: 91 additions & 0 deletions drivers/fuzz/src/fuzz_target_lms.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Licensed under the Apache-2.0 license

#![cfg_attr(feature = "libfuzzer-sys", no_main)]

#[cfg(all(not(feature = "libfuzzer-sys"), not(feature = "afl")))]
compile_error!("Either feature \"libfuzzer-sys\" or \"afl\" must be enabled!");

#[cfg(feature = "libfuzzer-sys")]
use libfuzzer_sys::fuzz_target;

#[cfg(feature = "afl")]
use afl::fuzz;

#[cfg(not(feature = "struct-aware"))]
use std::mem::size_of;

#[cfg(not(feature = "struct-aware"))]
use zerocopy::FromBytes;

use caliptra_drivers::Lms;
mod sha256;
use sha256::Sha256SoftwareDriver;

// For consistency with the ROM, use its type definitions
use caliptra_image_types::{ImageLmsPublicKey, ImageLmsSignature};

#[cfg(feature = "struct-aware")]
#[derive(arbitrary::Arbitrary, Debug)]
struct StructuredInput<'a> {
pub_key: ImageLmsPublicKey,
sig: ImageLmsSignature,
input: &'a [u8],
}

#[cfg(feature = "struct-aware")]
fn harness_structured(args: StructuredInput) {
let _result = Lms::default().verify_lms_signature_generic(
&mut Sha256SoftwareDriver::new(),
args.input,
&args.pub_key,
&args.sig,
);
}

#[cfg(not(feature = "struct-aware"))]
fn harness_unstructured(data: &[u8]) {
if data.len() < (size_of::<ImageLmsPublicKey>() + size_of::<ImageLmsSignature>()) {
return;
}

// The corpus is seeded with (pub_key, sig, input), so parse the data in this order
let mut offset = 0;
let pub_key = ImageLmsPublicKey::read_from_prefix(data).unwrap();
offset += size_of::<ImageLmsPublicKey>();
let sig = ImageLmsSignature::read_from_prefix(&data[offset..]).unwrap();
offset += size_of::<ImageLmsSignature>();
let input = &data[offset..];

let _result = Lms::default().verify_lms_signature_generic(
&mut Sha256SoftwareDriver::new(),
input,
&pub_key,
&sig,
);
}

// cargo-fuzz target
#[cfg(all(feature = "libfuzzer-sys", not(feature = "struct-aware")))]
fuzz_target!(|data: &[u8]| {
harness_unstructured(data);
});

#[cfg(all(feature = "libfuzzer-sys", feature = "struct-aware"))]
fuzz_target!(|data: StructuredInput| {
harness_structured(data);
});

// cargo-afl target
#[cfg(all(feature = "afl", not(feature = "struct-aware")))]
fn main() {
fuzz!(|data: &[u8]| {
harness_unstructured(data);
});
}

#[cfg(all(feature = "afl", feature = "struct-aware"))]
fn main() {
fuzz!(|data: StructuredInput| {
harness_structured(data);
});
}
47 changes: 47 additions & 0 deletions drivers/fuzz/src/sha256.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Licensed under the Apache-2.0 license

use caliptra_drivers::{Array4x8, CaliptraResult, Sha256Alg, Sha256DigestOp};
use std::marker::PhantomData;

use sha2::Digest;

#[derive(Default)]
pub struct Sha256SoftwareDriver {}

pub struct Sha256DigestOpSw<'a> {
driver: PhantomData<&'a mut Sha256SoftwareDriver>,
digest: sha2::Sha256,
}
impl<'a> Sha256DigestOp<'a> for Sha256DigestOpSw<'a> {
fn update(&mut self, data: &[u8]) -> CaliptraResult<()> {
self.digest.update(data);
Ok(())
}
fn finalize(self, digest: &mut Array4x8) -> CaliptraResult<()> {
let result = self.digest.finalize();
*digest = Array4x8::from(<[u8; 32]>::try_from(result.as_slice()).unwrap());
Ok(())
}
}

impl Sha256Alg for Sha256SoftwareDriver {
type DigestOp<'a> = Sha256DigestOpSw<'a>;

fn digest(&mut self, buf: &[u8]) -> CaliptraResult<Array4x8> {
let result = sha2::Sha256::digest(buf);
Ok(Array4x8::from(<[u8; 32]>::try_from(result).unwrap()))
}

fn digest_init(&mut self) -> CaliptraResult<Self::DigestOp<'_>> {
Ok(Sha256DigestOpSw {
driver: PhantomData::default(),
digest: sha2::Sha256::new(),
})
}
}

impl Sha256SoftwareDriver {
pub fn new() -> Self {
Self {}
}
}
2 changes: 1 addition & 1 deletion drivers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ pub use persistent::{
FUSE_LOG_MAX_COUNT, MEASUREMENT_MAX_COUNT, PCR_LOG_MAX_COUNT,
};
pub use sha1::{Sha1, Sha1Digest, Sha1DigestOp};
pub use sha256::{Sha256, Sha256DigestOp};
pub use sha256::{Sha256, Sha256Alg, Sha256DigestOp};
pub use sha384::{Sha384, Sha384Digest, Sha384DigestOp};
pub use sha384acc::{Sha384Acc, Sha384AccOp};
pub use soc_ifc::{report_boot_status, Lifecycle, MfgFlags, ResetReason, SocIfc};
Expand Down
10 changes: 5 additions & 5 deletions drivers/src/lms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Abstract:

use core::mem::MaybeUninit;

use crate::{Array4x8, CaliptraResult, Sha256};
use crate::{sha256::Sha256Alg, Array4x8, CaliptraResult, Sha256, Sha256DigestOp};
use caliptra_error::CaliptraError;
use caliptra_lms_types::{
LmotsAlgorithmType, LmsAlgorithmType, LmsIdentifier, LmsPublicKey, LmsSignature,
Expand Down Expand Up @@ -283,7 +283,7 @@ impl Lms {

pub fn hash_message<const N: usize>(
&self,
sha256_driver: &mut Sha256,
sha256_driver: &mut impl Sha256Alg,
message: &[u8],
lms_identifier: &LmsIdentifier,
q: &[u8; 4],
Expand All @@ -302,7 +302,7 @@ impl Lms {

pub fn candidate_ots_signature<const N: usize, const P: usize>(
&self,
sha256_driver: &mut Sha256,
sha256_driver: &mut impl Sha256Alg,
lms_identifier: &LmsIdentifier,
algo_type: LmotsAlgorithmType,
q: &[u8; 4],
Expand Down Expand Up @@ -401,7 +401,7 @@ impl Lms {
/// If glitch protection is needed, use `verify_lms_signature_cfi_generic` instead.
pub fn verify_lms_signature_generic<const N: usize, const P: usize, const H: usize>(
&self,
sha256_driver: &mut Sha256,
sha256_driver: &mut impl Sha256Alg,
input_string: &[u8],
lms_public_key: &LmsPublicKey<N>,
lms_sig: &LmsSignature<N, P, H>,
Expand Down Expand Up @@ -442,7 +442,7 @@ impl Lms {
#[inline(always)]
pub fn verify_lms_signature_cfi_generic<const N: usize, const P: usize, const H: usize>(
&self,
sha256_driver: &mut Sha256,
sha256_driver: &mut impl Sha256Alg,
input_string: &[u8],
lms_public_key: &LmsPublicKey<N>,
lms_sig: &LmsSignature<N, P, H>,
Expand Down
39 changes: 30 additions & 9 deletions drivers/src/sha256.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@ const SHA256_BLOCK_BYTE_SIZE: usize = 64;
const SHA256_BLOCK_LEN_OFFSET: usize = 56;
const SHA256_MAX_DATA_SIZE: usize = 1024 * 1024;

pub trait Sha256DigestOp<'a> {
fn update(&mut self, data: &[u8]) -> CaliptraResult<()>;
fn finalize(self, digest: &mut Array4x8) -> CaliptraResult<()>;
}

pub trait Sha256Alg {
type DigestOp<'a>: Sha256DigestOp<'a>
where
Self: 'a;

fn digest_init(&mut self) -> CaliptraResult<Self::DigestOp<'_>>;
fn digest(&mut self, buf: &[u8]) -> CaliptraResult<Array4x8>;
}

pub struct Sha256 {
sha256: Sha256Reg,
}
Expand All @@ -29,13 +43,18 @@ impl Sha256 {
pub fn new(sha256: Sha256Reg) -> Self {
Self { sha256 }
}
}

impl Sha256Alg for Sha256 {
type DigestOp<'a> = Sha256DigestOpHw<'a>;

/// Initialize multi step digest operation
///
/// # Returns
///
/// * `Sha256Digest` - Object representing the digest operation
pub fn digest_init(&mut self) -> CaliptraResult<Sha256DigestOp<'_>> {
let op = Sha256DigestOp {
fn digest_init(&mut self) -> CaliptraResult<Sha256DigestOpHw<'_>> {
let op = Sha256DigestOpHw {
sha: self,
state: Sha256DigestState::Init,
buf: [0u8; SHA256_BLOCK_BYTE_SIZE],
Expand All @@ -51,7 +70,7 @@ impl Sha256 {
/// # Arguments
///
/// * `buf` - Buffer to calculate the digest over
pub fn digest(&mut self, buf: &[u8]) -> CaliptraResult<Array4x8> {
fn digest(&mut self, buf: &[u8]) -> CaliptraResult<Array4x8> {
// Check if the buffer is not large
if buf.len() > SHA256_MAX_DATA_SIZE {
return Err(CaliptraError::DRIVER_SHA256_MAX_DATA);
Expand Down Expand Up @@ -96,7 +115,8 @@ impl Sha256 {

Ok(digest)
}

}
impl Sha256 {
/// Take a raw sha256 digest of 0 or more 64-byte blocks of memory. Unlike
/// digest(), the each word is passed to the sha256 peripheral without
/// byte-swapping to reverse the peripheral's big-endian words. This means the
Expand Down Expand Up @@ -255,7 +275,7 @@ enum Sha256DigestState {
}

/// Multi step SHA-256 digest operation
pub struct Sha256DigestOp<'a> {
pub struct Sha256DigestOpHw<'a> {
/// SHA-256 Engine
sha: &'a mut Sha256,

Expand All @@ -272,13 +292,13 @@ pub struct Sha256DigestOp<'a> {
data_size: usize,
}

impl<'a> Sha256DigestOp<'a> {
impl<'a> Sha256DigestOp<'a> for Sha256DigestOpHw<'a> {
/// Update the digest with data
///
/// # Arguments
///
/// * `data` - Data to used to update the digest
pub fn update(&mut self, data: &[u8]) -> CaliptraResult<()> {
fn update(&mut self, data: &[u8]) -> CaliptraResult<()> {
if self.state == Sha256DigestState::Final {
return Err(CaliptraError::DRIVER_SHA256_INVALID_STATE);
}
Expand Down Expand Up @@ -311,7 +331,7 @@ impl<'a> Sha256DigestOp<'a> {
}

/// Finalize the digest operations
pub fn finalize(mut self, digest: &mut Array4x8) -> CaliptraResult<()> {
fn finalize(mut self, digest: &mut Array4x8) -> CaliptraResult<()> {
if self.state == Sha256DigestState::Final {
return Err(CaliptraError::DRIVER_SHA256_INVALID_STATE);
}
Expand All @@ -333,7 +353,8 @@ impl<'a> Sha256DigestOp<'a> {

Ok(())
}

}
impl<'a> Sha256DigestOpHw<'a> {
/// Check if this the first digest operation
fn is_first(&self) -> bool {
self.state == Sha256DigestState::Init
Expand Down
2 changes: 1 addition & 1 deletion drivers/test-fw/src/bin/sha256_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Abstract:
#![no_std]
#![no_main]

use caliptra_drivers::{Array4x8, Sha256};
use caliptra_drivers::{Array4x8, Sha256, Sha256Alg, Sha256DigestOp};
use caliptra_kat::Sha256Kat;
use caliptra_registers::sha256::Sha256Reg;

Expand Down
2 changes: 1 addition & 1 deletion fmc/src/flow/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use caliptra_common::{crypto::Ecc384KeyPair, keyids::KEY_ID_TMP};
use caliptra_drivers::{
hmac384_kdf, okref, Array4x12, Array4x5, Array4x8, CaliptraResult, Ecc384PrivKeyIn,
Ecc384PrivKeyOut, Ecc384PubKey, Ecc384Result, Ecc384Signature, KeyId, KeyReadArgs, KeyUsage,
KeyWriteArgs,
KeyWriteArgs, Sha256Alg,
};

pub enum Crypto {}
Expand Down
2 changes: 1 addition & 1 deletion kat/src/sha256_kat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Abstract:
--*/

use caliptra_drivers::{Array4x8, CaliptraError, CaliptraResult, Sha256};
use caliptra_drivers::{Array4x8, CaliptraError, CaliptraResult, Sha256, Sha256Alg};

const EXPECTED_DIGEST: Array4x8 = Array4x8::new([
0xe3b0c442, 0x98fc1c14, 0x9afbf4c8, 0x996fb924, 0x27ae41e4, 0x649b934c, 0xa495991b, 0x7852b855,
Expand Down

0 comments on commit 8d1f779

Please sign in to comment.