rullst_orm/privacy/
secret_serde.rs1use super::{
12 ENVELOPE_PREFIX, PrivacyError, SecretString, configured_key, current_key, current_key_id,
13 decrypt_envelope, encrypt_with_context, parse_envelope,
14};
15use serde::{Deserialize, Deserializer, Serialize, Serializer};
16use std::cell::Cell;
17
18const SERDE_CONTEXT: &[u8] = b"rullst-orm:secret-string:serde";
22
23thread_local! {
24 static REDACTION_DEPTH: Cell<usize> = const { Cell::new(0) };
25}
26
27struct RedactionScope;
28
29impl RedactionScope {
30 fn enter() -> Self {
31 REDACTION_DEPTH.with(|depth| depth.set(depth.get().saturating_add(1)));
32 Self
33 }
34}
35
36impl Drop for RedactionScope {
37 fn drop(&mut self) {
38 REDACTION_DEPTH.with(|depth| depth.set(depth.get().saturating_sub(1)));
39 }
40}
41
42#[doc(hidden)]
47pub fn with_redacted_secrets<R>(serialize: impl FnOnce() -> R) -> R {
48 let _scope = RedactionScope::enter();
49 serialize()
50}
51
52fn redacting() -> bool {
53 REDACTION_DEPTH.with(|depth| depth.get() > 0)
54}
55
56fn encrypt_serialized(plaintext: &str) -> Result<String, PrivacyError> {
57 let key_id = current_key_id()?;
58 let key = current_key()?;
59 encrypt_with_context(plaintext, &key, &key_id, SERDE_CONTEXT)
60}
61
62fn decrypt_serialized(encrypted: &str) -> Result<String, PrivacyError> {
63 let envelope = parse_envelope(encrypted)?;
64 let key = configured_key(envelope.key_id)?;
65 decrypt_envelope(&envelope, &key, SERDE_CONTEXT)
66}
67
68fn is_envelope(value: &str) -> bool {
69 value
70 .strip_prefix(ENVELOPE_PREFIX)
71 .is_some_and(|rest| rest.starts_with(':'))
72}
73
74impl Serialize for SecretString {
75 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
78 if redacting() {
79 return serializer.serialize_str(crate::audit::REDACTED_VALUE);
80 }
81 let envelope = encrypt_serialized(&self.0).map_err(serde::ser::Error::custom)?;
82 serializer.serialize_str(&envelope)
83 }
84}
85
86impl<'de> Deserialize<'de> for SecretString {
87 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
91 let value = String::deserialize(deserializer)?;
92 if !is_envelope(&value) {
93 return Ok(SecretString(value));
94 }
95 decrypt_serialized(&value)
96 .map(SecretString)
97 .map_err(serde::de::Error::custom)
98 }
99}
100
101const ENVELOPE_INPUT_ERROR: &str = "an encrypted RULLST envelope is not accepted as secret input";
102
103pub fn deserialize_plaintext_secret<'de, D: Deserializer<'de>>(
117 deserializer: D,
118) -> Result<SecretString, D::Error> {
119 plaintext(String::deserialize(deserializer)?)
120}
121
122pub fn deserialize_optional_plaintext_secret<'de, D: Deserializer<'de>>(
126 deserializer: D,
127) -> Result<Option<SecretString>, D::Error> {
128 Option::<String>::deserialize(deserializer)?
129 .map(plaintext)
130 .transpose()
131}
132
133fn plaintext<E: serde::de::Error>(value: String) -> Result<SecretString, E> {
134 if is_envelope(&value) {
135 return Err(E::custom(ENVELOPE_INPUT_ERROR));
136 }
137 Ok(SecretString(value))
138}