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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//! Volatile zeroing of secret material.
//!
//! Each byte is written with `ptr::write_volatile` followed by a
//! `core::sync::atomic::compiler_fence(SeqCst)` to prevent the compiler
//! from eliding the zero-fill as a dead store. This is the same
//! technique used by the `zeroize` crate.
use ManuallyDrop;
use ;
/// Zero the contents of a byte slice using volatile writes.
///
/// The compiler cannot elide this — each byte is written via
/// `ptr::write_volatile`, and a compiler fence ensures the writes
/// are treated as observable side effects.
/// Zero a fixed-size array using volatile writes.
///
/// A convenience wrapper that forwards to [`zeroize_bytes`] over the
/// array as a slice; the same volatile-write plus compiler-fence
/// guarantee therefore applies, so the zero-fill cannot be elided as
/// a dead store.
/// Drop the value held in `slot`, then volatile-zero the storage it
/// occupied.
///
/// This is the escape hatch for a secret whose type belongs to another
/// crate: `hiss` cannot reach inside such a value to wipe its fields, and a
/// `Drop` impl that assigns zeros with ordinary stores is precisely the
/// pattern LLVM is entitled to delete once the storage is about to be
/// released — which, under LTO, it does. Hold the value in a
/// [`ManuallyDrop`] field, call this from the owning type's `Drop`, and the
/// foreign destructor still runs (step 1) but every byte the value occupied
/// is afterwards wiped with the same volatile write plus compiler fence
/// [`zeroize_bytes`] uses (step 2), whatever that destructor did or did not
/// do.
///
/// This is how [`AesGcmKey`](crate::noise::AesGcmKey) meets the
/// [`Cipher::Key`](crate::noise::Cipher::Key) scrub-on-drop contract over
/// `cryptoxide`'s `AesGcm256`.
///
/// # Caller obligations
///
/// This function is **safe to call only under a contract it cannot check**,
/// which is why it is crate-private rather than part of `hiss`'s public API:
///
/// * Call it **exactly once** per value, from the owning type's `Drop`. A
/// second call re-runs `T`'s destructor over all-zero bytes — freeing a
/// null pointer, for anything that owns an allocation.
/// * **Never touch `slot` again** afterwards. The value has been dropped and
/// its bytes overwritten; `ManuallyDrop`'s safe `Deref` would hand out a
/// dangling `T`. Calling this from the owner's `Drop` on a
/// `ManuallyDrop` field satisfies this by construction, because the
/// owner's drop glue skips that field and nothing else can reach it.
///
/// `T`'s layout, padding included, is irrelevant: step 2 only ever *writes*
/// bytes — through a raw pointer, forming no reference — to storage the slot
/// owns and whose value has already been dropped, so no byte is ever read
/// and nothing here depends on `T` having initialised all of them.
pub