mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +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:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -1658,6 +1658,7 @@ dependencies = [
|
|||||||
"ff-group-tests",
|
"ff-group-tests",
|
||||||
"group 0.13.0",
|
"group 0.13.0",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
|
"rustversion",
|
||||||
"sha2 0.9.9",
|
"sha2 0.9.9",
|
||||||
"subtle",
|
"subtle",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
@@ -1995,6 +1996,7 @@ dependencies = [
|
|||||||
"k256",
|
"k256",
|
||||||
"multiexp",
|
"multiexp",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
|
"rustversion",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
@@ -2798,7 +2800,10 @@ dependencies = [
|
|||||||
"blake2",
|
"blake2",
|
||||||
"digest 0.10.6",
|
"digest 0.10.6",
|
||||||
"merlin 3.0.0",
|
"merlin 3.0.0",
|
||||||
|
"rustversion",
|
||||||
"sha2 0.10.6",
|
"sha2 0.10.6",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5046,6 +5051,7 @@ dependencies = [
|
|||||||
"hex",
|
"hex",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
|
"rustversion",
|
||||||
"subtle",
|
"subtle",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
@@ -5278,6 +5284,7 @@ dependencies = [
|
|||||||
"group 0.13.0",
|
"group 0.13.0",
|
||||||
"k256",
|
"k256",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
|
"rustversion",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -13,12 +13,15 @@ all-features = true
|
|||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rand_core = "0.6"
|
rustversion = "1"
|
||||||
digest = "0.10"
|
|
||||||
|
|
||||||
zeroize = { version = "^1.5", features = ["zeroize_derive"] }
|
zeroize = { version = "^1.5", features = ["zeroize_derive"] }
|
||||||
subtle = "^2.4"
|
subtle = "^2.4"
|
||||||
|
|
||||||
|
rand_core = "0.6"
|
||||||
|
|
||||||
|
digest = "0.10"
|
||||||
|
|
||||||
ff = "0.13"
|
ff = "0.13"
|
||||||
group = "0.13"
|
group = "0.13"
|
||||||
|
|
||||||
@@ -29,6 +32,3 @@ curve25519-dalek = "^3.2"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
ff-group-tests = { path = "../ff-group-tests" }
|
ff-group-tests = { path = "../ff-group-tests" }
|
||||||
|
|
||||||
[features]
|
|
||||||
black_box = []
|
|
||||||
|
|||||||
@@ -38,14 +38,11 @@ use group::{
|
|||||||
mod field;
|
mod field;
|
||||||
pub use field::FieldElement;
|
pub use field::FieldElement;
|
||||||
|
|
||||||
// Feature gated due to MSRV requirements
|
// Use black_box when possible
|
||||||
#[cfg(feature = "black_box")]
|
#[rustversion::since(1.66)]
|
||||||
pub(crate) fn black_box<T>(val: T) -> T {
|
use core::hint::black_box;
|
||||||
core::hint::black_box(val)
|
#[rustversion::before(1.66)]
|
||||||
}
|
fn black_box<T>(val: T) -> T {
|
||||||
|
|
||||||
#[cfg(not(feature = "black_box"))]
|
|
||||||
pub(crate) fn black_box<T>(val: T) -> T {
|
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ all-features = true
|
|||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
rustversion = "1"
|
||||||
|
|
||||||
thiserror = { version = "1", optional = true }
|
thiserror = { version = "1", optional = true }
|
||||||
rand_core = "0.6"
|
rand_core = "0.6"
|
||||||
|
|
||||||
@@ -41,7 +43,6 @@ std = []
|
|||||||
serialize = ["std"]
|
serialize = ["std"]
|
||||||
|
|
||||||
# Needed for cross-group DLEqs
|
# Needed for cross-group DLEqs
|
||||||
black_box = []
|
|
||||||
secure_capacity_difference = []
|
secure_capacity_difference = []
|
||||||
experimental = ["std", "thiserror", "multiexp"]
|
experimental = ["std", "thiserror", "multiexp"]
|
||||||
|
|
||||||
|
|||||||
@@ -30,14 +30,11 @@ pub(crate) mod aos;
|
|||||||
mod bits;
|
mod bits;
|
||||||
use bits::{BitSignature, Bits};
|
use bits::{BitSignature, Bits};
|
||||||
|
|
||||||
// Feature gated due to MSRV requirements
|
// Use black_box when possible
|
||||||
#[cfg(feature = "black_box")]
|
#[rustversion::since(1.66)]
|
||||||
pub(crate) fn black_box<T>(val: T) -> T {
|
use core::hint::black_box;
|
||||||
core::hint::black_box(val)
|
#[rustversion::before(1.66)]
|
||||||
}
|
fn black_box<T>(val: T) -> T {
|
||||||
|
|
||||||
#[cfg(not(feature = "black_box"))]
|
|
||||||
pub(crate) fn black_box<T>(val: T) -> T {
|
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ all-features = true
|
|||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
rustversion = "1"
|
||||||
|
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
|
|
||||||
rand_core = "0.6"
|
rand_core = "0.6"
|
||||||
@@ -30,6 +32,3 @@ crypto-bigint = { version = "0.5", features = ["zeroize"] }
|
|||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
|
|
||||||
ff-group-tests = { path = "../ff-group-tests" }
|
ff-group-tests = { path = "../ff-group-tests" }
|
||||||
|
|
||||||
[features]
|
|
||||||
black_box = []
|
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
// Feature gated due to MSRV requirements
|
// Use black_box when possible
|
||||||
#[cfg(feature = "black_box")]
|
#[rustversion::since(1.66)]
|
||||||
pub(crate) fn black_box<T>(val: T) -> T {
|
use core::hint::black_box;
|
||||||
core::hint::black_box(val)
|
#[rustversion::before(1.66)]
|
||||||
}
|
fn black_box<T>(val: T) -> T {
|
||||||
|
|
||||||
#[cfg(not(feature = "black_box"))]
|
|
||||||
pub(crate) fn black_box<T>(val: T) -> T {
|
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ all-features = true
|
|||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
rustversion = "1"
|
||||||
|
|
||||||
zeroize = { version = "^1.5", features = ["zeroize_derive"] }
|
zeroize = { version = "^1.5", features = ["zeroize_derive"] }
|
||||||
|
|
||||||
ff = "0.13"
|
ff = "0.13"
|
||||||
@@ -27,5 +29,4 @@ k256 = { version = "0.13", features = ["bits"] }
|
|||||||
dalek-ff-group = { path = "../dalek-ff-group" }
|
dalek-ff-group = { path = "../dalek-ff-group" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
black_box = []
|
|
||||||
batch = ["rand_core"]
|
batch = ["rand_core"]
|
||||||
|
|||||||
@@ -22,14 +22,11 @@ pub use batch::BatchVerifier;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
// Feature gated due to MSRV requirements
|
// Use black_box when possible
|
||||||
#[cfg(feature = "black_box")]
|
#[rustversion::since(1.66)]
|
||||||
pub(crate) fn black_box<T>(val: T) -> T {
|
use core::hint::black_box;
|
||||||
core::hint::black_box(val)
|
#[rustversion::before(1.66)]
|
||||||
}
|
fn black_box<T>(val: T) -> T {
|
||||||
|
|
||||||
#[cfg(not(feature = "black_box"))]
|
|
||||||
pub(crate) fn black_box<T>(val: T) -> T {
|
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -139,18 +139,12 @@ impl<C: Ciphersuite> SchnorrAggregate<C> {
|
|||||||
|
|
||||||
/// A signature aggregator capable of consuming signatures in order to produce an aggregate.
|
/// A signature aggregator capable of consuming signatures in order to produce an aggregate.
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Zeroize)]
|
||||||
pub struct SchnorrAggregator<C: Ciphersuite> {
|
pub struct SchnorrAggregator<C: Ciphersuite> {
|
||||||
digest: DigestTranscript<C::H>,
|
digest: DigestTranscript<C::H>,
|
||||||
sigs: Vec<SchnorrSignature<C>>,
|
sigs: Vec<SchnorrSignature<C>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Ciphersuite> Zeroize for SchnorrAggregator<C> {
|
|
||||||
fn zeroize(&mut self) {
|
|
||||||
self.sigs.zeroize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: Ciphersuite> SchnorrAggregator<C> {
|
impl<C: Ciphersuite> SchnorrAggregator<C> {
|
||||||
/// Create a new aggregator.
|
/// Create a new aggregator.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ all-features = true
|
|||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
rustversion = "1"
|
||||||
|
|
||||||
|
subtle = "^2.4"
|
||||||
|
zeroize = "^1.5"
|
||||||
|
|
||||||
digest = "0.10"
|
digest = "0.10"
|
||||||
|
|
||||||
blake2 = { version = "0.10", optional = true }
|
blake2 = { version = "0.10", optional = true }
|
||||||
|
|||||||
@@ -2,6 +2,15 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
///! A transcript trait valid over a variety of transcript formats.
|
///! 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")]
|
#[cfg(feature = "merlin")]
|
||||||
mod merlin;
|
mod merlin;
|
||||||
@@ -12,13 +21,6 @@ pub use crate::merlin::MerlinTranscript;
|
|||||||
#[cfg(any(test, feature = "tests"))]
|
#[cfg(any(test, feature = "tests"))]
|
||||||
pub mod 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.
|
/// A transcript trait valid over a variety of transcript formats.
|
||||||
pub trait Transcript: Send + Clone {
|
pub trait Transcript: Send + Clone {
|
||||||
type Challenge: Send + Sync + Clone + AsRef<[u8]>;
|
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.
|
/// The recommended transcript, guaranteed to be secure against length-extension attacks.
|
||||||
#[cfg(feature = "recommended")]
|
#[cfg(feature = "recommended")]
|
||||||
pub type RecommendedTranscript = DigestTranscript<blake2::Blake2b512>;
|
pub type RecommendedTranscript = DigestTranscript<blake2::Blake2b512>;
|
||||||
|
|||||||
Reference in New Issue
Block a user