1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//! [`SecretString`] — a `String` newtype with redacted [`Debug`].
//!
//! Used by [`super::types::S3Credentials::Static`] to hold AWS access
//! keys, secret keys, and session tokens. The wrapper exists so that an
//! incidental `tracing::debug!(?cfg)`, panic backtrace formatter, or
//! stray `dbg!()` cannot leak the credential into logs. The codebase
//! already has redaction discipline for HTTP-origin URL credentials
//! (see `decdn_config_types::redact_for_log`) — this is the same pattern for
//! TOML-borne secrets.
//!
//! - [`std::fmt::Debug`] always prints `"***"` regardless of the
//! wrapped value.
//! - `std::fmt::Display` is **not** implemented — use
//! [`SecretString::expose`] when you need the raw value, so every
//! call site is greppable.
//! - [`serde::Deserialize`] is implemented (so TOML still works).
//! - [`serde::Serialize`] is implemented but emits a redacted,
//! hashed form (`"hash:{digest}"`) — never the cleartext secret.
//! The config section structs derive `Serialize` (for symmetry with
//! `Deserialize`), so `SecretString` must implement it for them to
//! compile; emitting a hash rather than the raw string is
//! defense-in-depth, the `Serialize` analogue of the `Debug`
//! redaction above — any incidental serialization of a config
//! section (diagnostics, tests, future call sites) cannot leak the
//! credential. The hash uses
//! [`std::collections::hash_map::DefaultHasher`], which `std`
//! documents as producing the same output for all `DefaultHasher`
//! instances in a given build of the standard library — i.e.
//! deterministic across processes built with the same toolchain, so
//! distinct secrets almost always serialize to distinct forms. It is
//! non-cryptographic (single-pair collision probability ≈ 2⁻⁶⁴ for a
//! 64-bit hash); cryptographic strength is unnecessary because the
//! output only guards against accidental cleartext exposure, never
//! authenticates anything.
use ;
use ;
/// String newtype that redacts itself in [`Debug`] output. See module
/// docs for the threat model.
;
// PartialEq is provided to keep tests ergonomic. Comparing two
// SecretStrings for equality does not leak: the comparison runs in
// constant-ish time at the byte level and produces only a `bool`.
// This is intentionally not constant-time crypto: we're guarding
// against incidental leakage via Debug/Serialize, not against side-
// channel attackers.