@@ -184,18 +184,70 @@ impl KeyConfig {
184
184
}
185
185
}
186
186
187
- /// Application secrets
187
+ /// Encryption config option.
188
+ #[ derive( Debug , Clone ) ]
189
+ pub enum Encryption {
190
+ File ( Utf8PathBuf ) ,
191
+ Value ( [ u8 ; 32 ] ) ,
192
+ }
193
+
194
+ /// Encryption fields as serialized in JSON.
188
195
#[ serde_as]
189
- #[ derive( Debug , Clone , Serialize , Deserialize , JsonSchema ) ]
190
- pub struct SecretsConfig {
191
- /// Encryption key for secure cookies
196
+ #[ derive( JsonSchema , Serialize , Deserialize , Debug , Clone ) ]
197
+ struct EncryptionRaw {
198
+ /// File containing the encryption key for secure cookies.
199
+ #[ schemars( with = "Option<String>" ) ]
200
+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
201
+ encryption_file : Option < Utf8PathBuf > ,
202
+
203
+ /// Encryption key for secure cookies.
192
204
#[ schemars(
193
- with = "String" ,
205
+ with = "Option< String> " ,
194
206
regex( pattern = r"[0-9a-fA-F]{64}" ) ,
195
207
example = "example_secret"
196
208
) ]
197
- #[ serde_as( as = "serde_with::hex::Hex" ) ]
198
- pub encryption : [ u8 ; 32 ] ,
209
+ #[ serde_as( as = "Option<serde_with::hex::Hex>" ) ]
210
+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
211
+ encryption : Option < [ u8 ; 32 ] > ,
212
+ }
213
+
214
+ impl TryFrom < EncryptionRaw > for Encryption {
215
+ type Error = anyhow:: Error ;
216
+
217
+ fn try_from ( value : EncryptionRaw ) -> Result < Encryption , Self :: Error > {
218
+ match ( value. encryption , value. encryption_file ) {
219
+ ( None , None ) => bail ! ( "Missing `encryption` or `encryption_file`" ) ,
220
+ ( None , Some ( path) ) => Ok ( Encryption :: File ( path) ) ,
221
+ ( Some ( encryption) , None ) => Ok ( Encryption :: Value ( encryption) ) ,
222
+ ( Some ( _) , Some ( _) ) => bail ! ( "Cannot specify both `encryption` and `encryption_file`" ) ,
223
+ }
224
+ }
225
+ }
226
+
227
+ impl From < Encryption > for EncryptionRaw {
228
+ fn from ( value : Encryption ) -> Self {
229
+ match value {
230
+ Encryption :: File ( path) => EncryptionRaw {
231
+ encryption_file : Some ( path) ,
232
+ encryption : None ,
233
+ } ,
234
+ Encryption :: Value ( encryption) => EncryptionRaw {
235
+ encryption_file : None ,
236
+ encryption : Some ( encryption) ,
237
+ } ,
238
+ }
239
+ }
240
+ }
241
+
242
+ /// Application secrets
243
+ #[ serde_as]
244
+ #[ derive( Debug , Clone , Serialize , Deserialize , JsonSchema ) ]
245
+ pub struct SecretsConfig {
246
+ /// Encryption key for secure cookies
247
+ #[ schemars( with = "EncryptionRaw" ) ]
248
+ #[ serde_as( as = "serde_with::TryFromInto<EncryptionRaw>" ) ]
249
+ #[ serde( flatten) ]
250
+ encryption : Encryption ,
199
251
200
252
/// List of private keys to use for signing and encrypting payloads
201
253
#[ serde( default ) ]
@@ -216,9 +268,33 @@ impl SecretsConfig {
216
268
}
217
269
218
270
/// Derive an [`Encrypter`] out of the config
219
- #[ must_use]
220
- pub fn encrypter ( & self ) -> Encrypter {
221
- Encrypter :: new ( & self . encryption )
271
+ ///
272
+ /// # Errors
273
+ ///
274
+ /// Returns an error when the Encryptor can not be created.
275
+ pub async fn encrypter ( & self ) -> anyhow:: Result < Encrypter > {
276
+ Ok ( Encrypter :: new ( & self . encryption ( ) . await ?) )
277
+ }
278
+
279
+ /// Returns the encryption secret.
280
+ ///
281
+ /// # Errors
282
+ ///
283
+ /// Returns an error when the encryption secret could not be read from file.
284
+ pub async fn encryption ( & self ) -> anyhow:: Result < [ u8 ; 32 ] > {
285
+ // Read the encryption secret either embedded in the config file or on disk
286
+ match self . encryption {
287
+ Encryption :: Value ( encryption) => Ok ( encryption) ,
288
+ Encryption :: File ( ref path) => {
289
+ let mut bytes = [ 0 ; 32 ] ;
290
+ let content = tokio:: fs:: read ( path) . await ?;
291
+ hex:: decode_to_slice ( content, & mut bytes) . context (
292
+ "Content of `encryption_file` must contain hex characters \
293
+ encoding exactly 32 bytes",
294
+ ) ?;
295
+ Ok ( bytes)
296
+ }
297
+ }
222
298
}
223
299
}
224
300
@@ -299,7 +375,7 @@ impl SecretsConfig {
299
375
} ;
300
376
301
377
Ok ( Self {
302
- encryption : Standard . sample ( & mut rng) ,
378
+ encryption : Encryption :: Value ( Standard . sample ( & mut rng) ) ,
303
379
keys : vec ! [ rsa_key, ec_p256_key, ec_p384_key, ec_k256_key] ,
304
380
} )
305
381
}
@@ -340,7 +416,7 @@ impl SecretsConfig {
340
416
} ;
341
417
342
418
Self {
343
- encryption : [ 0xEA ; 32 ] ,
419
+ encryption : Encryption :: Value ( [ 0xEA ; 32 ] ) ,
344
420
keys : vec ! [ rsa_key, ecdsa_key] ,
345
421
}
346
422
}
0 commit comments