From ed6312689ce533c48a9ae58a9fd5f9e271fe5d37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kate=C5=99ina=20Churanov=C3=A1?= Date: Tue, 12 Sep 2023 00:02:34 +0200 Subject: [PATCH] Add timing-resistant-secret-traits feature for PartialEq/Hash (#232) Derived implementations of `PartialEq` and `Hash` are susceptible to timing side channels that make them unsuitable for secret types. This change introduces timing-safe implementations of `PartialEq` and `Hash` to all secret types that compare/hash, respectively, the SHA-256 hash of the secret rather than the secret itself. Because these implementations are significantly more computationally expensive than ordinary `PartialEq` and `Hash` implementations, they're behind a non-default `timing-resistant-secret-traits` feature flag. --- Cargo.toml | 1 + src/types.rs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index afe0922..09a9278 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ default = ["reqwest", "rustls-tls"] pkce-plain = [] native-tls = ["reqwest/native-tls"] rustls-tls = ["reqwest/rustls-tls"] +timing-resistant-secret-traits = [] [dependencies] base64 = "0.13" diff --git a/src/types.rs b/src/types.rs index b11d827..398353f 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,6 +1,8 @@ use std::convert::Into; use std::fmt::Error as FormatterError; use std::fmt::{Debug, Formatter}; +#[cfg(feature = "timing-resistant-secret-traits")] +use std::hash::{Hash, Hasher}; use std::ops::Deref; use rand::{thread_rng, Rng}; @@ -148,6 +150,7 @@ macro_rules! new_secret_type { $( #[$attr] )* + #[cfg_attr(feature = "timing-resistant-secret-traits", derive(Eq))] pub struct $name($type); impl $name { $($item)* @@ -170,6 +173,21 @@ macro_rules! new_secret_type { write!(f, concat!(stringify!($name), "([redacted])")) } } + + #[cfg(feature = "timing-resistant-secret-traits")] + impl PartialEq for $name { + fn eq(&self, other: &Self) -> bool { + Sha256::digest(&self.0) == Sha256::digest(&other.0) + } + } + + #[cfg(feature = "timing-resistant-secret-traits")] + impl Hash for $name { + fn hash(&self, state: &mut H) { + Sha256::digest(&self.0).hash(state) + } + } + }; }