Skip to content

Commit

Permalink
Fixes and moves the SHA256 busy check (#689)
Browse files Browse the repository at this point in the history
Inside `libraries/opensk`, we want to preserve the property that there
are never two SHA256 hashes calculates in parallel. This is useful to
support hardware crypto that can't swap out its state.

To check this property, there was an assertion in `libraries/crypto`.
However, the assertion

- didn't work,
- should have been in opensk instead
- and should run in tests, but not when deployed.

Since Rust runs tests in parallel, this PR makes sure that the assertion
only fails if two SHA256 are calculated within one thread.

Fixes #688
  • Loading branch information
kaczmarczyck authored Jul 3, 2024
1 parent 8a0ee9d commit 19063c4
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 10 deletions.
9 changes: 0 additions & 9 deletions libraries/crypto/src/sha256.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,11 @@
use super::{Hash256, HashBlockSize64Bytes};
use arrayref::{array_mut_ref, array_ref};
use byteorder::{BigEndian, ByteOrder};
use core::cell::Cell;
use core::num::Wrapping;
use zeroize::Zeroize;

const BLOCK_SIZE: usize = 64;

// To be able to support hardware cryptography, we want to make sure we never compute multiple
// sha256 in parallel. (Note that almost all usage of Sha256 is through Hash256::hash which is
// statically correct. There's only 2 low-level usages in the `hmac::hmac_256` and those are
// sequential.) This variable tracks whether `new` was called but `finalize` wasn't called yet.
const BUSY: Cell<bool> = Cell::new(false);

pub struct Sha256 {
state: [Wrapping<u32>; 8],
block: [u8; BLOCK_SIZE],
Expand All @@ -46,7 +39,6 @@ impl Drop for Sha256 {

impl Hash256 for Sha256 {
fn new() -> Self {
assert!(!BUSY.replace(true));
Sha256 {
state: Sha256::H,
block: [0; BLOCK_SIZE],
Expand Down Expand Up @@ -112,7 +104,6 @@ impl Hash256 for Sha256 {
for i in 0..8 {
BigEndian::write_u32(array_mut_ref![output, 4 * i, 4], self.state[i].0);
}
BUSY.set(false);
}
}

Expand Down
24 changes: 24 additions & 0 deletions libraries/opensk/src/api/crypto/rust_crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ use aes::cipher::{
BlockDecrypt, BlockDecryptMut, BlockEncrypt, BlockEncryptMut, KeyInit, KeyIvInit,
};
use alloc::vec::Vec;
#[cfg(test)]
use core::cell::RefCell;
use core::convert::TryFrom;
use hmac::digest::FixedOutput;
use hmac::Mac;
Expand All @@ -43,6 +45,18 @@ use p256::ecdsa::signature::{SignatureEncoding, Signer, Verifier};
use p256::ecdsa::{SigningKey, VerifyingKey};
use p256::elliptic_curve::sec1::ToEncodedPoint;
use sha2::Digest;
#[cfg(test)]
use std::sync::{Mutex, MutexGuard};

// To be able to support hardware cryptography, we want to make sure we never compute multiple
// sha256 in parallel. Note that calling `digest` or `digest_mut` is statically correct.
// These variables track whether `new` was called but `finalize` wasn't called yet.
#[cfg(test)]
static BUSY: Mutex<()> = Mutex::new(());
#[cfg(test)]
thread_local! {
static BUSY_GUARD: RefCell<Option<MutexGuard<'static, ()>>> = RefCell::new(None);
}

pub struct SoftwareCrypto;
pub struct SoftwareEcdh;
Expand Down Expand Up @@ -224,6 +238,11 @@ impl Sha256 for SoftwareSha256 {
}

fn new() -> Self {
#[cfg(test)]
BUSY_GUARD.with_borrow_mut(|guard| {
assert!(guard.is_none());
*guard = Some(BUSY.lock().unwrap());
});
let hasher = sha2::Sha256::new();
Self { hasher }
}
Expand All @@ -236,6 +255,11 @@ impl Sha256 for SoftwareSha256 {
/// Finalizes the hashing process, returns the hash value.
fn finalize(self, output: &mut [u8; HASH_SIZE]) {
FixedOutput::finalize_into(self.hasher, output.into());
#[cfg(test)]
BUSY_GUARD.with_borrow_mut(|guard| {
assert!(guard.is_some());
*guard = None;
});
}
}

Expand Down
26 changes: 25 additions & 1 deletion libraries/opensk/src/api/crypto/software_crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,23 @@ use crate::api::crypto::{
};
use crate::api::rng::Rng;
use alloc::vec::Vec;
#[cfg(test)]
use core::cell::RefCell;
use crypto::Hash256;
#[cfg(test)]
use std::sync::{Mutex, MutexGuard};
use zeroize::Zeroize;

// To be able to support hardware cryptography, we want to make sure we never compute multiple
// sha256 in parallel. Note that calling `digest` or `digest_mut` is statically correct.
// These variables track whether `new` was called but `finalize` wasn't called yet.
#[cfg(test)]
static BUSY: Mutex<()> = Mutex::new(());
#[cfg(test)]
thread_local! {
static BUSY_GUARD: RefCell<Option<MutexGuard<'static, ()>>> = RefCell::new(None);
}

/// Cryptography implementation using our own library of primitives.
///
/// Warning: The used library does not implement zeroization.
Expand Down Expand Up @@ -184,6 +198,11 @@ pub struct SoftwareSha256 {

impl Sha256 for SoftwareSha256 {
fn new() -> Self {
#[cfg(test)]
BUSY_GUARD.with_borrow_mut(|guard| {
assert!(guard.is_none());
*guard = Some(BUSY.lock().unwrap());
});
let hasher = crypto::sha256::Sha256::new();
Self { hasher }
}
Expand All @@ -195,7 +214,12 @@ impl Sha256 for SoftwareSha256 {

/// Finalizes the hashing process, returns the hash value.
fn finalize(self, output: &mut [u8; HASH_SIZE]) {
self.hasher.finalize(output)
self.hasher.finalize(output);
#[cfg(test)]
BUSY_GUARD.with_borrow_mut(|guard| {
assert!(guard.is_some());
*guard = None;
});
}
}

Expand Down

0 comments on commit 19063c4

Please sign in to comment.