From fc770d72df680d8efb6646f35c4a41d517fa07de Mon Sep 17 00:00:00 2001 From: Ethan Brierley Date: Sat, 23 Mar 2024 14:45:35 +0000 Subject: [PATCH] feat: impl `Deref` and `DerefMut` without exposing `Secret` I'm not sure what the usecase for this is. Originally I thought it was required for https://github.com/eopb/redact/pull/58 but I was wrong. It was fun implementing this with a bit of unsafe but I don't know if I'll merge it. --- Cargo.toml | 1 + src/lib.rs | 1 - src/ops.rs | 37 ++++++++++++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 47a09ba..ee2e703 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ serde = ["dep:serde"] zeroize = ["dep:zeroize"] [dependencies] +bytemuck = { version = "1.15", default-features = false } fake = { version = "2.5", optional = true, default-features = false } rand = { version = "0.8", optional = true, default-features = false } serde = { version = "1.0", optional = true, default-features = false } diff --git a/src/lib.rs b/src/lib.rs index 152d96c..8bc912d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ #![doc = include_str!("../README.md")] #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(not(feature = "std"), no_std)] -#![forbid(unsafe_code)] #[cfg(feature = "std")] mod error; diff --git a/src/ops.rs b/src/ops.rs index 7aece84..3b732bd 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -1,6 +1,9 @@ use crate::Secret; -use core::ops; +use core::{ + ops, + ops::{Deref, DerefMut}, +}; macro_rules! ops { { ($type:tt, $trait:ident, $method:ident), $($tt:tt)* } => { @@ -69,3 +72,35 @@ ops! { (unary, Neg, neg), (unary, Not, not), } + +use bytemuck::TransparentWrapper; + +/// We introduce this private wrapper around `Secret` to safely implement [TransparentWrapper] without +/// inadvertently providing a way for `Secret`s to be exposed without using `Secret::expose_secret` +#[repr(transparent)] +struct Wrapper(Secret); + +// SAFETY: `Secret` and `Wrapper` both contains only a single field and are `#[repr(transparent)]`. +// This meets the documented requirements [bytemuck::TransparentWrapper] as long as we +// do not override any of its methods. +unsafe impl bytemuck::TransparentWrapper for Wrapper {} + +impl Deref for Secret +where + T: Deref, +{ + type Target = Secret; + + fn deref(&self) -> &Self::Target { + &Wrapper::wrap_ref(self.0.deref()).0 + } +} + +impl DerefMut for Secret +where + T: DerefMut, +{ + fn deref_mut(&mut self) -> &mut Secret { + &mut Wrapper::wrap_mut(self.0.deref_mut()).0 + } +}