@@ -9,13 +9,9 @@ use std::borrow::Cow;
9
9
use anyhow:: { Context , bail} ;
10
10
use camino:: Utf8PathBuf ;
11
11
use futures_util:: future:: { try_join, try_join_all} ;
12
- use mas_jose:: jwk:: { JsonWebKey , JsonWebKeySet } ;
12
+ use mas_jose:: jwk:: { JsonWebKey , JsonWebKeySet , Thumbprint } ;
13
13
use mas_keystore:: { Encrypter , Keystore , PrivateKey } ;
14
- use rand:: {
15
- Rng , SeedableRng ,
16
- distributions:: { Alphanumeric , DistString , Standard } ,
17
- prelude:: Distribution as _,
18
- } ;
14
+ use rand:: { Rng , SeedableRng , distributions:: Standard , prelude:: Distribution as _} ;
19
15
use schemars:: JsonSchema ;
20
16
use serde:: { Deserialize , Serialize } ;
21
17
use serde_with:: serde_as;
@@ -132,7 +128,11 @@ impl From<Key> for KeyRaw {
132
128
#[ serde_as]
133
129
#[ derive( JsonSchema , Serialize , Deserialize , Clone , Debug ) ]
134
130
pub struct KeyConfig {
135
- kid : String ,
131
+ /// The key ID `kid` of the key as used by JWKs.
132
+ ///
133
+ /// If not given, `kid` will be the key’s RFC 7638 JWK Thumbprint.
134
+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
135
+ kid : Option < String > ,
136
136
137
137
#[ schemars( with = "PasswordRaw" ) ]
138
138
#[ serde_as( as = "serde_with::TryFromInto<PasswordRaw>" ) ]
@@ -178,8 +178,13 @@ impl KeyConfig {
178
178
None => PrivateKey :: load ( & key) ?,
179
179
} ;
180
180
181
+ let kid = match self . kid . clone ( ) {
182
+ Some ( kid) => kid,
183
+ None => private_key. thumbprint_sha256_base64 ( ) ,
184
+ } ;
185
+
181
186
Ok ( JsonWebKey :: new ( private_key)
182
- . with_kid ( self . kid . clone ( ) )
187
+ . with_kid ( kid)
183
188
. with_use ( mas_iana:: jose:: JsonWebKeyUse :: Sig ) )
184
189
}
185
190
}
@@ -322,7 +327,7 @@ impl SecretsConfig {
322
327
. await
323
328
. context ( "could not join blocking task" ) ?;
324
329
let rsa_key = KeyConfig {
325
- kid : Alphanumeric . sample_string ( & mut rng , 10 ) ,
330
+ kid : None ,
326
331
password : None ,
327
332
key : Key :: Value ( rsa_key. to_pem ( pem_rfc7468:: LineEnding :: LF ) ?. to_string ( ) ) ,
328
333
} ;
@@ -338,7 +343,7 @@ impl SecretsConfig {
338
343
. await
339
344
. context ( "could not join blocking task" ) ?;
340
345
let ec_p256_key = KeyConfig {
341
- kid : Alphanumeric . sample_string ( & mut rng , 10 ) ,
346
+ kid : None ,
342
347
password : None ,
343
348
key : Key :: Value ( ec_p256_key. to_pem ( pem_rfc7468:: LineEnding :: LF ) ?. to_string ( ) ) ,
344
349
} ;
@@ -354,7 +359,7 @@ impl SecretsConfig {
354
359
. await
355
360
. context ( "could not join blocking task" ) ?;
356
361
let ec_p384_key = KeyConfig {
357
- kid : Alphanumeric . sample_string ( & mut rng , 10 ) ,
362
+ kid : None ,
358
363
password : None ,
359
364
key : Key :: Value ( ec_p384_key. to_pem ( pem_rfc7468:: LineEnding :: LF ) ?. to_string ( ) ) ,
360
365
} ;
@@ -370,7 +375,7 @@ impl SecretsConfig {
370
375
. await
371
376
. context ( "could not join blocking task" ) ?;
372
377
let ec_k256_key = KeyConfig {
373
- kid : Alphanumeric . sample_string ( & mut rng , 10 ) ,
378
+ kid : None ,
374
379
password : None ,
375
380
key : Key :: Value ( ec_k256_key. to_pem ( pem_rfc7468:: LineEnding :: LF ) ?. to_string ( ) ) ,
376
381
} ;
@@ -383,7 +388,7 @@ impl SecretsConfig {
383
388
384
389
pub ( crate ) fn test ( ) -> Self {
385
390
let rsa_key = KeyConfig {
386
- kid : "abcdef" . to_owned ( ) ,
391
+ kid : None ,
387
392
password : None ,
388
393
key : Key :: Value (
389
394
indoc:: indoc! { r"
@@ -402,7 +407,7 @@ impl SecretsConfig {
402
407
) ,
403
408
} ;
404
409
let ecdsa_key = KeyConfig {
405
- kid : "ghijkl" . to_owned ( ) ,
410
+ kid : None ,
406
411
password : None ,
407
412
key : Key :: Value (
408
413
indoc:: indoc! { r"
@@ -422,3 +427,68 @@ impl SecretsConfig {
422
427
}
423
428
}
424
429
}
430
+
431
+ #[ cfg( test) ]
432
+ mod tests {
433
+ use figment:: {
434
+ Figment , Jail ,
435
+ providers:: { Format , Yaml } ,
436
+ } ;
437
+ use mas_jose:: constraints:: Constrainable ;
438
+ use tokio:: { runtime:: Handle , task} ;
439
+
440
+ use super :: * ;
441
+
442
+ #[ tokio:: test]
443
+ async fn load_config_inline_secrets ( ) {
444
+ task:: spawn_blocking ( || {
445
+ Jail :: expect_with ( |jail| {
446
+ jail. create_file (
447
+ "config.yaml" ,
448
+ indoc:: indoc! { r"
449
+ secrets:
450
+ encryption: >-
451
+ 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff
452
+ keys:
453
+ - kid: lekid0
454
+ key: |
455
+ -----BEGIN EC PRIVATE KEY-----
456
+ MHcCAQEEIOtZfDuXZr/NC0V3sisR4Chf7RZg6a2dpZesoXMlsPeRoAoGCCqGSM49
457
+ AwEHoUQDQgAECfpqx64lrR85MOhdMxNmIgmz8IfmM5VY9ICX9aoaArnD9FjgkBIl
458
+ fGmQWxxXDSWH6SQln9tROVZaduenJqDtDw==
459
+ -----END EC PRIVATE KEY-----
460
+ - key: |
461
+ -----BEGIN EC PRIVATE KEY-----
462
+ MHcCAQEEIKlZz/GnH0idVH1PnAF4HQNwRafgBaE2tmyN1wjfdOQqoAoGCCqGSM49
463
+ AwEHoUQDQgAEHrgPeG+Mt8eahih1h4qaPjhl7jT25cdzBkg3dbVks6gBR2Rx4ug9
464
+ h27LAir5RqxByHvua2XsP46rSTChof78uw==
465
+ -----END EC PRIVATE KEY-----
466
+ " } ,
467
+ ) ?;
468
+
469
+ let config = Figment :: new ( )
470
+ . merge ( Yaml :: file ( "config.yaml" ) )
471
+ . extract_inner :: < SecretsConfig > ( "secrets" ) ?;
472
+
473
+ Handle :: current ( ) . block_on ( async move {
474
+ assert_eq ! (
475
+ config. encryption( ) . await . unwrap( ) ,
476
+ [
477
+ 0 , 0 , 17 , 17 , 34 , 34 , 51 , 51 , 68 , 68 , 85 , 85 , 102 , 102 , 119 , 119 , 136 ,
478
+ 136 , 153 , 153 , 170 , 170 , 187 , 187 , 204 , 204 , 221 , 221 , 238 , 238 , 255 ,
479
+ 255
480
+ ]
481
+ ) ;
482
+
483
+ let key_store = config. key_store ( ) . await . unwrap ( ) ;
484
+ assert ! ( key_store. iter( ) . any( |k| k. kid( ) == Some ( "lekid0" ) ) ) ;
485
+ assert ! ( key_store. iter( ) . any( |k| k. kid( ) == Some ( "ONUCn80fsiISFWKrVMEiirNVr-QEvi7uQI0QH9q9q4o" ) ) ) ;
486
+ } ) ;
487
+
488
+ Ok ( ( ) )
489
+ } ) ;
490
+ } )
491
+ . await
492
+ . unwrap ( ) ;
493
+ }
494
+ }
0 commit comments