3
3
4
4
#![ allow( clippy:: multiple_crate_versions) ]
5
5
6
+ use std:: { fs:: File , io:: Read , path:: PathBuf } ;
7
+
8
+ use reqwest:: { blocking:: ClientBuilder , Certificate } ;
9
+
6
10
#[ derive( thiserror:: Error , PartialEq , Eq ) ]
7
11
pub enum Error {
8
12
#[ error( "configuration error: {0}" ) ]
@@ -25,6 +29,12 @@ impl From<reqwest::Error> for Error {
25
29
}
26
30
}
27
31
32
+ impl From < std:: io:: Error > for Error {
33
+ fn from ( re : std:: io:: Error ) -> Self {
34
+ Error :: ConfigError ( re. to_string ( ) )
35
+ }
36
+ }
37
+
28
38
impl From < jsonwebkey:: ConversionError > for Error {
29
39
fn from ( e : jsonwebkey:: ConversionError ) -> Self {
30
40
Error :: DataConversionError ( e. to_string ( ) )
@@ -54,35 +64,56 @@ type EvidenceCreationCb = fn(nonce: &[u8], accepted: &[String]) -> Result<(Vec<u
54
64
/// A builder for ChallengeResponse objects
55
65
pub struct ChallengeResponseBuilder {
56
66
new_session_url : Option < String > ,
57
- // TODO(tho) add TLS config / authn tokens etc.
67
+ root_certificate : Option < PathBuf > ,
58
68
}
59
69
60
70
impl ChallengeResponseBuilder {
61
71
/// default constructor
62
72
pub fn new ( ) -> Self {
63
73
Self {
64
74
new_session_url : None ,
75
+ root_certificate : None ,
65
76
}
66
77
}
67
78
68
79
/// Use this method to supply the URL of the verification endpoint that will create
69
- /// new challenge-response sessions, e.g.
80
+ /// new challenge-response sessions, e.g.:
70
81
/// "https://veraison.example/challenge-response/v1/newSession".
71
82
pub fn with_new_session_url ( mut self , v : String ) -> ChallengeResponseBuilder {
72
83
self . new_session_url = Some ( v) ;
73
84
self
74
85
}
75
86
87
+ /// Use this method to add a custom root certificate. For example, this can
88
+ /// be used to connect to a server that has a self-signed certificate which
89
+ /// is not present in (and does not need to be added to) the system's trust
90
+ /// anchor store.
91
+ pub fn with_root_certificate ( mut self , v : PathBuf ) -> ChallengeResponseBuilder {
92
+ self . root_certificate = Some ( v) ;
93
+ self
94
+ }
95
+
76
96
/// Instantiate a valid ChallengeResponse object, or fail with an error.
77
97
pub fn build ( self ) -> Result < ChallengeResponse , Error > {
78
98
let new_session_url_str = self
79
99
. new_session_url
80
100
. ok_or_else ( || Error :: ConfigError ( "missing API endpoint" . to_string ( ) ) ) ?;
81
101
102
+ let mut http_client_builder: ClientBuilder = reqwest:: blocking:: ClientBuilder :: new ( ) ;
103
+
104
+ if self . root_certificate . is_some ( ) {
105
+ let mut buf = Vec :: new ( ) ;
106
+ File :: open ( self . root_certificate . unwrap ( ) ) ?. read_to_end ( & mut buf) ?;
107
+ let cert = Certificate :: from_pem ( & buf) ?;
108
+ http_client_builder = http_client_builder. add_root_certificate ( cert) ;
109
+ }
110
+
111
+ let http_client = http_client_builder. use_rustls_tls ( ) . build ( ) ?;
112
+
82
113
Ok ( ChallengeResponse {
83
114
new_session_url : url:: Url :: parse ( & new_session_url_str)
84
115
. map_err ( |e| Error :: ConfigError ( e. to_string ( ) ) ) ?,
85
- http_client : reqwest :: blocking :: Client :: builder ( ) . build ( ) ? ,
116
+ http_client,
86
117
} )
87
118
}
88
119
}
@@ -330,6 +361,68 @@ pub struct VerificationApi {
330
361
api_endpoints : std:: collections:: HashMap < String , String > ,
331
362
}
332
363
364
+ /// A builder for Discovery objects
365
+ pub struct DiscoveryBuilder {
366
+ url : Option < String > ,
367
+ root_certificate : Option < PathBuf > ,
368
+ }
369
+
370
+ impl DiscoveryBuilder {
371
+ /// default constructor
372
+ pub fn new ( ) -> Self {
373
+ Self {
374
+ url : None ,
375
+ root_certificate : None ,
376
+ }
377
+ }
378
+
379
+ /// Use this method to supply the URL of the discovery endpoint, e.g.:
380
+ /// "https://veraison.example/.well-known/veraison/verification"
381
+ pub fn with_url ( mut self , v : String ) -> DiscoveryBuilder {
382
+ self . url = Some ( v) ;
383
+ self
384
+ }
385
+
386
+ /// Use this method to add a custom root certificate. For example, this can
387
+ /// be used to connect to a server that has a self-signed certificate which
388
+ /// is not present in (and does not need to be added to) the system's trust
389
+ /// anchor store.
390
+ pub fn with_root_certificate ( mut self , v : PathBuf ) -> DiscoveryBuilder {
391
+ self . root_certificate = Some ( v) ;
392
+ self
393
+ }
394
+
395
+ /// Instantiate a valid Discovery object, or fail with an error.
396
+ pub fn build ( self ) -> Result < Discovery , Error > {
397
+ let url = self
398
+ . url
399
+ . ok_or_else ( || Error :: ConfigError ( "missing API endpoint" . to_string ( ) ) ) ?;
400
+
401
+ let mut http_client_builder: ClientBuilder = reqwest:: blocking:: ClientBuilder :: new ( ) ;
402
+
403
+ if self . root_certificate . is_some ( ) {
404
+ let mut buf = Vec :: new ( ) ;
405
+ File :: open ( self . root_certificate . unwrap ( ) ) ?. read_to_end ( & mut buf) ?;
406
+ let cert = Certificate :: from_pem ( & buf) ?;
407
+ http_client_builder = http_client_builder. add_root_certificate ( cert) ;
408
+ }
409
+
410
+ let http_client = http_client_builder. use_rustls_tls ( ) . build ( ) ?;
411
+
412
+ Ok ( Discovery {
413
+ verification_url : url:: Url :: parse ( & url)
414
+ . map_err ( |e| Error :: ConfigError ( e. to_string ( ) ) ) ?,
415
+ http_client,
416
+ } )
417
+ }
418
+ }
419
+
420
+ impl Default for DiscoveryBuilder {
421
+ fn default ( ) -> Self {
422
+ Self :: new ( )
423
+ }
424
+ }
425
+
333
426
impl VerificationApi {
334
427
/// Obtains the EAR verification public key encoded in ASN.1 DER format.
335
428
pub fn ear_verification_key_as_der ( & self ) -> Result < Vec < u8 > , Error > {
@@ -406,31 +499,27 @@ impl VerificationApi {
406
499
/// This structure allows Veraison endpoints and service capabilities to be discovered
407
500
/// dynamically.
408
501
///
409
- /// Use [`Discovery::from_base_url() `] to create an instance of this structure for the
502
+ /// Use [`DiscoveryBuilder `] to create an instance of this structure for the
410
503
/// Veraison service instance that you are communicating with.
411
504
pub struct Discovery {
412
- provisioning_url : url:: Url , //TODO: The provisioning URL discovery is not implemented yet.
413
505
verification_url : url:: Url ,
414
506
http_client : reqwest:: blocking:: Client ,
415
507
}
416
508
417
509
impl Discovery {
510
+ #[ deprecated( since = "0.0.2" , note = "please use the `DiscoveryBuilder` instead" ) ]
418
511
/// Establishes client API discovery for the Veraison service instance running at the
419
512
/// given base URL.
420
513
pub fn from_base_url ( base_url_str : String ) -> Result < Discovery , Error > {
421
514
let base_url =
422
515
url:: Url :: parse ( & base_url_str) . map_err ( |e| Error :: ConfigError ( e. to_string ( ) ) ) ?;
423
516
424
- let mut provisioning_url = base_url. clone ( ) ;
425
- provisioning_url. set_path ( ".well-known/veraison/provisioning" ) ;
426
-
427
517
let mut verification_url = base_url;
428
518
verification_url. set_path ( ".well-known/veraison/verification" ) ;
429
519
430
520
Ok ( Discovery {
431
- provisioning_url,
432
521
verification_url,
433
- http_client : reqwest:: blocking:: Client :: builder ( ) . build ( ) ? ,
522
+ http_client : reqwest:: blocking:: Client :: new ( ) ,
434
523
} )
435
524
}
436
525
@@ -621,7 +710,15 @@ mod tests {
621
710
. mount ( & mock_server)
622
711
. await ;
623
712
624
- let discovery = Discovery :: from_base_url ( mock_server. uri ( ) )
713
+ let discovery_api_endpoint = format ! (
714
+ "{}{}" ,
715
+ mock_server. uri( ) ,
716
+ "/.well-known/veraison/verification"
717
+ ) ;
718
+
719
+ let discovery = DiscoveryBuilder :: new ( )
720
+ . with_url ( discovery_api_endpoint)
721
+ . build ( )
625
722
. expect ( "Failed to create Discovery client." ) ;
626
723
627
724
let verification_api = discovery
0 commit comments