1
1
//! The "encrypted age identity file" identity type.
2
2
3
- use std:: { cell :: Cell , io } ;
3
+ use std:: { io , ops :: DerefMut , sync :: Mutex } ;
4
4
5
5
use i18n_embed_fl:: fl;
6
6
@@ -77,12 +77,12 @@ impl<R: io::Read> IdentityState<R> {
77
77
78
78
/// An encrypted age identity file.
79
79
pub struct Identity < R : io:: Read , C : Callbacks > {
80
- state : Cell < IdentityState < R > > ,
80
+ state : Mutex < Option < IdentityState < R > > > ,
81
81
filename : Option < String > ,
82
82
callbacks : C ,
83
83
}
84
84
85
- impl < R : io:: Read , C : Callbacks + Clone + ' static > Identity < R , C > {
85
+ impl < R : io:: Read , C : Callbacks + Clone + Send + Sync + ' static > Identity < R , C > {
86
86
/// Parses an encrypted identity from an input containing valid UTF-8.
87
87
///
88
88
/// `filename` is the path to the file that the input is reading from, if any.
@@ -98,10 +98,10 @@ impl<R: io::Read, C: Callbacks + Clone + 'static> Identity<R, C> {
98
98
match Decryptor :: new ( data) ? {
99
99
Decryptor :: Recipients ( _) => Ok ( None ) ,
100
100
Decryptor :: Passphrase ( decryptor) => Ok ( Some ( Identity {
101
- state : Cell :: new ( IdentityState :: Encrypted {
101
+ state : Mutex :: new ( Some ( IdentityState :: Encrypted {
102
102
decryptor,
103
103
max_work_factor,
104
- } ) ,
104
+ } ) ) ,
105
105
filename,
106
106
callbacks,
107
107
} ) ) ,
@@ -113,9 +113,11 @@ impl<R: io::Read, C: Callbacks + Clone + 'static> Identity<R, C> {
113
113
/// If this encrypted identity has not been decrypted yet, calling this method will
114
114
/// trigger a passphrase request.
115
115
pub fn recipients ( & self ) -> Result < Vec < Box < dyn crate :: Recipient > > , EncryptError > {
116
- match self
117
- . state
116
+ let mut state = self . state . lock ( ) . unwrap ( ) ;
117
+ match state
118
+ . deref_mut ( )
118
119
. take ( )
120
+ . expect ( "We never leave this set to None" )
119
121
. decrypt ( self . filename . as_deref ( ) , self . callbacks . clone ( ) )
120
122
{
121
123
Ok ( ( identities, _) ) => {
@@ -124,11 +126,11 @@ impl<R: io::Read, C: Callbacks + Clone + 'static> Identity<R, C> {
124
126
. map ( |entry| entry. to_recipient ( self . callbacks . clone ( ) ) )
125
127
. collect :: < Result < Vec < _ > , _ > > ( ) ;
126
128
127
- self . state . set ( IdentityState :: Decrypted ( identities) ) ;
129
+ * state = Some ( IdentityState :: Decrypted ( identities) ) ;
128
130
recipients
129
131
}
130
132
Err ( e) => {
131
- self . state . set ( IdentityState :: Poisoned ( Some ( e. clone ( ) ) ) ) ;
133
+ * state = Some ( IdentityState :: Poisoned ( Some ( e. clone ( ) ) ) ) ;
132
134
Err ( EncryptError :: EncryptedIdentities ( e) )
133
135
}
134
136
}
@@ -151,12 +153,14 @@ impl<R: io::Read, C: Callbacks + Clone + 'static> Identity<R, C> {
151
153
) -> Option < Result < age_core:: format:: FileKey , DecryptError > >
152
154
where
153
155
F : Fn (
154
- Result < Box < dyn crate :: Identity > , DecryptError > ,
156
+ Result < Box < dyn crate :: Identity + Send + Sync > , DecryptError > ,
155
157
) -> Option < Result < age_core:: format:: FileKey , DecryptError > > ,
156
158
{
157
- match self
158
- . state
159
+ let mut state = self . state . lock ( ) . unwrap ( ) ;
160
+ match state
161
+ . deref_mut ( )
159
162
. take ( )
163
+ . expect ( "We never leave this set to None" )
160
164
. decrypt ( self . filename . as_deref ( ) , self . callbacks . clone ( ) )
161
165
{
162
166
Ok ( ( identities, requested_passphrase) ) => {
@@ -175,18 +179,18 @@ impl<R: io::Read, C: Callbacks + Clone + 'static> Identity<R, C> {
175
179
) ) ;
176
180
}
177
181
178
- self . state . set ( IdentityState :: Decrypted ( identities) ) ;
182
+ * state = Some ( IdentityState :: Decrypted ( identities) ) ;
179
183
result
180
184
}
181
185
Err ( e) => {
182
- self . state . set ( IdentityState :: Poisoned ( Some ( e. clone ( ) ) ) ) ;
186
+ * state = Some ( IdentityState :: Poisoned ( Some ( e. clone ( ) ) ) ) ;
183
187
Some ( Err ( e) )
184
188
}
185
189
}
186
190
}
187
191
}
188
192
189
- impl < R : io:: Read , C : Callbacks + Clone + ' static > crate :: Identity for Identity < R , C > {
193
+ impl < R : io:: Read , C : Callbacks + Clone + Send + Sync + ' static > crate :: Identity for Identity < R , C > {
190
194
fn unwrap_stanza (
191
195
& self ,
192
196
stanza : & age_core:: format:: Stanza ,
@@ -210,7 +214,7 @@ impl<R: io::Read, C: Callbacks + Clone + 'static> crate::Identity for Identity<R
210
214
211
215
#[ cfg( test) ]
212
216
mod tests {
213
- use std:: { cell :: Cell , io :: BufReader } ;
217
+ use std:: { io :: BufReader , sync :: Mutex } ;
214
218
215
219
use secrecy:: { ExposeSecret , SecretString } ;
216
220
@@ -235,12 +239,17 @@ fOrxrKTj7xCdNS3+OrCdnBC8Z9cKDxjCGWW3fkjLsYha0Jo=
235
239
236
240
const TEST_RECIPIENT : & str = "age1ysxuaeqlk7xd8uqsh8lsnfwt9jzzjlqf49ruhpjrrj5yatlcuf7qke4pqe" ;
237
241
238
- #[ derive( Clone ) ]
239
- struct MockCallbacks ( Cell < Option < & ' static str > > ) ;
242
+ struct MockCallbacks ( Mutex < Option < & ' static str > > ) ;
243
+
244
+ impl Clone for MockCallbacks {
245
+ fn clone ( & self ) -> Self {
246
+ Self ( Mutex :: new ( self . 0 . lock ( ) . unwrap ( ) . clone ( ) ) )
247
+ }
248
+ }
240
249
241
250
impl MockCallbacks {
242
251
fn new ( passphrase : & ' static str ) -> Self {
243
- MockCallbacks ( Cell :: new ( Some ( passphrase) ) )
252
+ MockCallbacks ( Mutex :: new ( Some ( passphrase) ) )
244
253
}
245
254
}
246
255
@@ -255,7 +264,9 @@ fOrxrKTj7xCdNS3+OrCdnBC8Z9cKDxjCGWW3fkjLsYha0Jo=
255
264
256
265
/// This intentionally panics if called twice.
257
266
fn request_passphrase ( & self , _: & str ) -> Option < secrecy:: SecretString > {
258
- Some ( SecretString :: new ( self . 0 . take ( ) . unwrap ( ) . to_owned ( ) ) )
267
+ Some ( SecretString :: new (
268
+ self . 0 . lock ( ) . unwrap ( ) . take ( ) . unwrap ( ) . to_owned ( ) ,
269
+ ) )
259
270
}
260
271
}
261
272
0 commit comments