From efb70cf00fffb55c93821eeda66f1e1d60a50118 Mon Sep 17 00:00:00 2001 From: "Andrew J. Stone" Date: Mon, 7 Apr 2025 21:46:42 +0000 Subject: [PATCH] Allow converting a `SecretSlice` into `SecretBox<[S; N]>` This PR allows moving the inner secret of a slice into the inner secret of an array without changing the actual allocation. If the conversion fails then the original value is returned. This prevents a caller from having to clone the secret if they want to further restrict its type. --- secrecy/src/lib.rs | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/secrecy/src/lib.rs b/secrecy/src/lib.rs index 2f1866fd..74105deb 100644 --- a/secrecy/src/lib.rs +++ b/secrecy/src/lib.rs @@ -38,6 +38,7 @@ extern crate alloc; use alloc::{boxed::Box, string::String, vec::Vec}; use core::convert::Infallible; +use core::mem; use core::str::FromStr; use core::{ any, @@ -207,6 +208,28 @@ where } } +impl TryFrom> for SecretBox<[S; N]> +where + S: Zeroize, + [S]: Zeroize, +{ + type Error = SecretSlice; + + fn try_from(mut value: SecretSlice) -> Result { + let mut temp: Box<[S]> = Box::new([]); + mem::swap(&mut temp, &mut value.inner_secret); + + match TryInto::>::try_into(temp) { + Ok(inner_secret) => Ok(SecretBox { inner_secret }), + Err(mut old) => { + // Put the secret back + mem::swap(&mut old, &mut value.inner_secret); + Err(value) + } + } + } +} + /// Secret string type. /// /// This is a type alias for [`SecretBox`] which supports some helpful trait impls. @@ -337,7 +360,8 @@ where #[cfg(test)] mod tests { - use crate::{ExposeSecret, SecretString}; + use crate::{ExposeSecret, SecretBox, SecretSlice, SecretString}; + use alloc::boxed::Box; use core::str::FromStr; #[test] @@ -345,4 +369,21 @@ mod tests { let secret = SecretString::from_str("test").unwrap(); assert_eq!(secret.expose_secret(), "test"); } + + #[test] + fn secret_slice_to_array() { + let secret = SecretSlice::new(Box::new([5, 6, 7, 8, 9u8])); + assert_eq!(secret.expose_secret(), &[5, 6, 7, 8, 9]); + + // Failure when the slice is a different size from the array. + let res = TryInto::>::try_into(secret); + assert!(res.is_err()); + // We get the original secret back + let secret = res.unwrap_err(); + assert_eq!(secret.expose_secret(), &[5, 6, 7, 8, 9]); + + // Success when the array is the same size as the slice + let secret_array: SecretBox<[u8; 5]> = secret.try_into().unwrap(); + assert_eq!(secret_array.expose_secret(), &[5, 6, 7, 8, 9]); + } }