mirror of
https://github.com/serai-dex/serai.git
synced 2025-03-12 09:26:51 +00:00
Resolve #268 by adding a Zeroize to DigestTranscript which writes a full block
This is a 'better-than-nothing' attempt to invalidate its state. Also replaces black_box features with usage of the rustversion crate.
This commit is contained in:
parent
79aff5d4c8
commit
47be373eb0
12 changed files with 108 additions and 56 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -1658,6 +1658,7 @@ dependencies = [
|
|||
"ff-group-tests",
|
||||
"group 0.13.0",
|
||||
"rand_core 0.6.4",
|
||||
"rustversion",
|
||||
"sha2 0.9.9",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
|
@ -1995,6 +1996,7 @@ dependencies = [
|
|||
"k256",
|
||||
"multiexp",
|
||||
"rand_core 0.6.4",
|
||||
"rustversion",
|
||||
"thiserror",
|
||||
"zeroize",
|
||||
]
|
||||
|
@ -2798,7 +2800,10 @@ dependencies = [
|
|||
"blake2",
|
||||
"digest 0.10.6",
|
||||
"merlin 3.0.0",
|
||||
"rustversion",
|
||||
"sha2 0.10.6",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5046,6 +5051,7 @@ dependencies = [
|
|||
"hex",
|
||||
"lazy_static",
|
||||
"rand_core 0.6.4",
|
||||
"rustversion",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
@ -5278,6 +5284,7 @@ dependencies = [
|
|||
"group 0.13.0",
|
||||
"k256",
|
||||
"rand_core 0.6.4",
|
||||
"rustversion",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
|
|
|
@ -13,12 +13,15 @@ all-features = true
|
|||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
rand_core = "0.6"
|
||||
digest = "0.10"
|
||||
rustversion = "1"
|
||||
|
||||
zeroize = { version = "^1.5", features = ["zeroize_derive"] }
|
||||
subtle = "^2.4"
|
||||
|
||||
rand_core = "0.6"
|
||||
|
||||
digest = "0.10"
|
||||
|
||||
ff = "0.13"
|
||||
group = "0.13"
|
||||
|
||||
|
@ -29,6 +32,3 @@ curve25519-dalek = "^3.2"
|
|||
|
||||
[dev-dependencies]
|
||||
ff-group-tests = { path = "../ff-group-tests" }
|
||||
|
||||
[features]
|
||||
black_box = []
|
||||
|
|
|
@ -38,14 +38,11 @@ use group::{
|
|||
mod field;
|
||||
pub use field::FieldElement;
|
||||
|
||||
// Feature gated due to MSRV requirements
|
||||
#[cfg(feature = "black_box")]
|
||||
pub(crate) fn black_box<T>(val: T) -> T {
|
||||
core::hint::black_box(val)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "black_box"))]
|
||||
pub(crate) fn black_box<T>(val: T) -> T {
|
||||
// Use black_box when possible
|
||||
#[rustversion::since(1.66)]
|
||||
use core::hint::black_box;
|
||||
#[rustversion::before(1.66)]
|
||||
fn black_box<T>(val: T) -> T {
|
||||
val
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ all-features = true
|
|||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
rustversion = "1"
|
||||
|
||||
thiserror = { version = "1", optional = true }
|
||||
rand_core = "0.6"
|
||||
|
||||
|
@ -41,7 +43,6 @@ std = []
|
|||
serialize = ["std"]
|
||||
|
||||
# Needed for cross-group DLEqs
|
||||
black_box = []
|
||||
secure_capacity_difference = []
|
||||
experimental = ["std", "thiserror", "multiexp"]
|
||||
|
||||
|
|
|
@ -30,14 +30,11 @@ pub(crate) mod aos;
|
|||
mod bits;
|
||||
use bits::{BitSignature, Bits};
|
||||
|
||||
// Feature gated due to MSRV requirements
|
||||
#[cfg(feature = "black_box")]
|
||||
pub(crate) fn black_box<T>(val: T) -> T {
|
||||
core::hint::black_box(val)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "black_box"))]
|
||||
pub(crate) fn black_box<T>(val: T) -> T {
|
||||
// Use black_box when possible
|
||||
#[rustversion::since(1.66)]
|
||||
use core::hint::black_box;
|
||||
#[rustversion::before(1.66)]
|
||||
fn black_box<T>(val: T) -> T {
|
||||
val
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ all-features = true
|
|||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
rustversion = "1"
|
||||
|
||||
lazy_static = "1"
|
||||
|
||||
rand_core = "0.6"
|
||||
|
@ -30,6 +32,3 @@ crypto-bigint = { version = "0.5", features = ["zeroize"] }
|
|||
hex = "0.4"
|
||||
|
||||
ff-group-tests = { path = "../ff-group-tests" }
|
||||
|
||||
[features]
|
||||
black_box = []
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
use zeroize::Zeroize;
|
||||
|
||||
// Feature gated due to MSRV requirements
|
||||
#[cfg(feature = "black_box")]
|
||||
pub(crate) fn black_box<T>(val: T) -> T {
|
||||
core::hint::black_box(val)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "black_box"))]
|
||||
pub(crate) fn black_box<T>(val: T) -> T {
|
||||
// Use black_box when possible
|
||||
#[rustversion::since(1.66)]
|
||||
use core::hint::black_box;
|
||||
#[rustversion::before(1.66)]
|
||||
fn black_box<T>(val: T) -> T {
|
||||
val
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ all-features = true
|
|||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
rustversion = "1"
|
||||
|
||||
zeroize = { version = "^1.5", features = ["zeroize_derive"] }
|
||||
|
||||
ff = "0.13"
|
||||
|
@ -27,5 +29,4 @@ k256 = { version = "0.13", features = ["bits"] }
|
|||
dalek-ff-group = { path = "../dalek-ff-group" }
|
||||
|
||||
[features]
|
||||
black_box = []
|
||||
batch = ["rand_core"]
|
||||
|
|
|
@ -22,14 +22,11 @@ pub use batch::BatchVerifier;
|
|||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
// Feature gated due to MSRV requirements
|
||||
#[cfg(feature = "black_box")]
|
||||
pub(crate) fn black_box<T>(val: T) -> T {
|
||||
core::hint::black_box(val)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "black_box"))]
|
||||
pub(crate) fn black_box<T>(val: T) -> T {
|
||||
// Use black_box when possible
|
||||
#[rustversion::since(1.66)]
|
||||
use core::hint::black_box;
|
||||
#[rustversion::before(1.66)]
|
||||
fn black_box<T>(val: T) -> T {
|
||||
val
|
||||
}
|
||||
|
||||
|
|
|
@ -139,18 +139,12 @@ impl<C: Ciphersuite> SchnorrAggregate<C> {
|
|||
|
||||
/// A signature aggregator capable of consuming signatures in order to produce an aggregate.
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Zeroize)]
|
||||
pub struct SchnorrAggregator<C: Ciphersuite> {
|
||||
digest: DigestTranscript<C::H>,
|
||||
sigs: Vec<SchnorrSignature<C>>,
|
||||
}
|
||||
|
||||
impl<C: Ciphersuite> Zeroize for SchnorrAggregator<C> {
|
||||
fn zeroize(&mut self) {
|
||||
self.sigs.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Ciphersuite> SchnorrAggregator<C> {
|
||||
/// Create a new aggregator.
|
||||
///
|
||||
|
|
|
@ -13,6 +13,11 @@ all-features = true
|
|||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
rustversion = "1"
|
||||
|
||||
subtle = "^2.4"
|
||||
zeroize = "^1.5"
|
||||
|
||||
digest = "0.10"
|
||||
|
||||
blake2 = { version = "0.10", optional = true }
|
||||
|
|
|
@ -2,6 +2,15 @@
|
|||
#![no_std]
|
||||
|
||||
///! A transcript trait valid over a variety of transcript formats.
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use digest::{
|
||||
typenum::{
|
||||
consts::U32, marker_traits::NonZero, type_operators::IsGreaterOrEqual, operator_aliases::GrEq,
|
||||
},
|
||||
core_api::BlockSizeUser,
|
||||
Digest, Output, HashMarker,
|
||||
};
|
||||
|
||||
#[cfg(feature = "merlin")]
|
||||
mod merlin;
|
||||
|
@ -12,13 +21,6 @@ pub use crate::merlin::MerlinTranscript;
|
|||
#[cfg(any(test, feature = "tests"))]
|
||||
pub mod tests;
|
||||
|
||||
use digest::{
|
||||
typenum::{
|
||||
consts::U32, marker_traits::NonZero, type_operators::IsGreaterOrEqual, operator_aliases::GrEq,
|
||||
},
|
||||
Digest, Output, HashMarker,
|
||||
};
|
||||
|
||||
/// A transcript trait valid over a variety of transcript formats.
|
||||
pub trait Transcript: Send + Clone {
|
||||
type Challenge: Send + Sync + Clone + AsRef<[u8]>;
|
||||
|
@ -134,6 +136,61 @@ impl<D: Send + Clone + SecureDigest> Transcript for DigestTranscript<D> {
|
|||
}
|
||||
}
|
||||
|
||||
// Digest doesn't implement Zeroize
|
||||
// Implement Zeroize for DigestTranscript by writing twice the block size to the digest in an
|
||||
// attempt to overwrite the internal hash state/any leftover bytes
|
||||
impl<D: Send + Clone + SecureDigest> Zeroize for DigestTranscript<D>
|
||||
where
|
||||
D: BlockSizeUser,
|
||||
{
|
||||
fn zeroize(&mut self) {
|
||||
// Update in 4-byte chunks to reduce call quantity and enable word-level update optimizations
|
||||
const WORD_SIZE: usize = 4;
|
||||
|
||||
// block_size returns the block_size in bytes
|
||||
// Use a ceil div in case the block size isn't evenly divisible by our word size
|
||||
let words = (D::block_size() + (WORD_SIZE - 1)) / WORD_SIZE;
|
||||
for _ in 0 .. (2 * words) {
|
||||
self.0.update([255; WORD_SIZE]);
|
||||
}
|
||||
|
||||
// Hopefully, the hash state is now overwritten to the point no data is recoverable
|
||||
// These writes may be optimized out if they're never read
|
||||
// Attempt to get them marked as read
|
||||
|
||||
#[rustversion::since(1.66)]
|
||||
fn mark_read<D: Send + Clone + SecureDigest>(transcript: &mut DigestTranscript<D>) {
|
||||
// Just get a challenge from the state
|
||||
let mut challenge = core::hint::black_box(transcript.0.clone().finalize());
|
||||
challenge.as_mut().zeroize();
|
||||
}
|
||||
|
||||
#[rustversion::before(1.66)]
|
||||
fn mark_read<D: Send + Clone + SecureDigest>(transcript: &mut DigestTranscript<D>) {
|
||||
// Get a challenge
|
||||
let challenge = transcript.0.clone().finalize();
|
||||
|
||||
// Attempt to use subtle's, non-exposed black_box function, by creating a Choice from this
|
||||
// challenge
|
||||
|
||||
let mut read = 0;
|
||||
for byte in challenge.as_ref() {
|
||||
read ^= byte;
|
||||
}
|
||||
challenge.as_mut().zeroize();
|
||||
|
||||
// Since this Choice isn't further read, its creation may be optimized out, including its
|
||||
// internal black_box
|
||||
// This remains our best attempt
|
||||
let mut choice = bool::from(subtle::Choice::from(read >> 7));
|
||||
read.zeroize();
|
||||
choice.zeroize();
|
||||
}
|
||||
|
||||
mark_read(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// The recommended transcript, guaranteed to be secure against length-extension attacks.
|
||||
#[cfg(feature = "recommended")]
|
||||
pub type RecommendedTranscript = DigestTranscript<blake2::Blake2b512>;
|
||||
|
|
Loading…
Reference in a new issue