From 19063c43d9aba3fe60bf5c90901c54d287418165 Mon Sep 17 00:00:00 2001 From: kaczmarczyck <43844792+kaczmarczyck@users.noreply.github.com> Date: Wed, 3 Jul 2024 16:17:42 +0200 Subject: [PATCH] Fixes and moves the SHA256 busy check (#689) 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 --- libraries/crypto/src/sha256.rs | 9 ------- .../opensk/src/api/crypto/rust_crypto.rs | 24 +++++++++++++++++ .../opensk/src/api/crypto/software_crypto.rs | 26 ++++++++++++++++++- 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/libraries/crypto/src/sha256.rs b/libraries/crypto/src/sha256.rs index 09e32e0d..cf98b753 100644 --- a/libraries/crypto/src/sha256.rs +++ b/libraries/crypto/src/sha256.rs @@ -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 = Cell::new(false); - pub struct Sha256 { state: [Wrapping; 8], block: [u8; BLOCK_SIZE], @@ -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], @@ -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); } } diff --git a/libraries/opensk/src/api/crypto/rust_crypto.rs b/libraries/opensk/src/api/crypto/rust_crypto.rs index c03932ad..59d6cea7 100644 --- a/libraries/opensk/src/api/crypto/rust_crypto.rs +++ b/libraries/opensk/src/api/crypto/rust_crypto.rs @@ -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; @@ -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>> = RefCell::new(None); +} pub struct SoftwareCrypto; pub struct SoftwareEcdh; @@ -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 } } @@ -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; + }); } } diff --git a/libraries/opensk/src/api/crypto/software_crypto.rs b/libraries/opensk/src/api/crypto/software_crypto.rs index 6ce9bbb1..89c73d24 100644 --- a/libraries/opensk/src/api/crypto/software_crypto.rs +++ b/libraries/opensk/src/api/crypto/software_crypto.rs @@ -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>> = RefCell::new(None); +} + /// Cryptography implementation using our own library of primitives. /// /// Warning: The used library does not implement zeroization. @@ -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 } } @@ -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; + }); } }