11//! Certificate generation configuration.
2- //!
2+
33use std:: { collections:: HashMap , net:: IpAddr , sync:: LazyLock } ;
44
55use serde:: { Deserialize , Serialize } ;
6+ use serde_with:: { OneOrMany , formats:: PreferOne , serde_as} ;
67
78/// This is the root structure that contains all certificate chains.
89#[ derive( Debug , Serialize , Deserialize , PartialEq , Eq ) ]
@@ -25,18 +26,18 @@ pub struct CertificateAuthorityConfiguration {
2526}
2627
2728/// A certificate used for client authentication.
28- // NOTE: A shared basic cert configuration could come in handy to not have to duplicate all cert properties for clients and servers
29+ // NOTE: A shared basic cert configuration could come in handy to not have to duplicate
30+ // all cert properties for clients and servers.
2931#[ derive( Debug , Serialize , Deserialize , PartialEq , Eq ) ]
3032#[ serde( deny_unknown_fields) ]
3133pub struct ClientConfiguration {
3234 /// Enables the export of the private key file.
3335 #[ serde( default = "ClientConfiguration::default_export_key" ) ]
3436 pub export_key : bool ,
3537
36- /// Ip address of the client.
37- // TODO: maybe allow multiple Ip addresses?
38- pub ip : IpAddr ,
39- // TODO: Have a dns name that is used for the subject alt names
38+ /// Properties that will be set as Subject Alternative Names (SAN)s.
39+ #[ serde( flatten) ]
40+ pub subject_alternative_names : SubjectAlternativeNames ,
4041}
4142
4243/// A certificate used for server authentication.
@@ -47,10 +48,26 @@ pub struct ServerConfiguration {
4748 #[ serde( default = "ServerConfiguration::default_export_key" ) ]
4849 pub export_key : bool ,
4950
50- /// Ip address of the server.
51- // TODO: maybe allow multiple Ip addresses?
52- pub ip : IpAddr ,
53- // TODO: Have a dns name that is used for the subject alt names
51+ /// Properties that will be set as Subject Alternative Names (SAN)s.
52+ #[ serde( flatten) ]
53+ pub subject_alternative_names : SubjectAlternativeNames ,
54+ }
55+
56+ /// Values that will be set as Subject Alternative Names (SAN) of the generated certificate.
57+ ///
58+ /// [RFC 5280](https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.6) describes how dns names and
59+ /// IP addresses are handled in x509 certificates.
60+ #[ serde_as]
61+ #[ derive( Debug , Serialize , Deserialize , PartialEq , Eq ) ]
62+ #[ serde( deny_unknown_fields) ]
63+ pub struct SubjectAlternativeNames {
64+ /// Ip addresses of the client.
65+ #[ serde_as( as = "OneOrMany<_, PreferOne>" ) ]
66+ pub ip : Vec < IpAddr > ,
67+
68+ /// DNS names of the client
69+ #[ serde_as( as = "OneOrMany<_, PreferOne>" ) ]
70+ pub dns_name : Vec < String > ,
5471}
5572
5673/// All kinds of different certificates.
@@ -159,16 +176,22 @@ pub mod fixtures {
159176 /// Provides a [`CertificateType`] that is a client certificate.
160177 pub fn client_certificate_type ( ) -> CertificateType {
161178 CertificateType :: Client ( ClientConfiguration {
162- ip : IpAddr :: V4 ( Ipv4Addr :: LOCALHOST ) ,
163179 export_key : ClientConfiguration :: default_export_key ( ) ,
180+ subject_alternative_names : SubjectAlternativeNames {
181+ ip : vec ! [ IpAddr :: V4 ( Ipv4Addr :: LOCALHOST ) ] ,
182+ dns_name : vec ! [ "my-client.org" . to_string( ) ] ,
183+ } ,
164184 } )
165185 }
166186
167187 /// Provides a [`CertificateType`] that is a server certificate.
168188 pub fn server_certificate_type ( ) -> CertificateType {
169189 CertificateType :: Server ( ServerConfiguration {
170- ip : IpAddr :: V4 ( Ipv4Addr :: LOCALHOST ) ,
171190 export_key : ServerConfiguration :: default_export_key ( ) ,
191+ subject_alternative_names : SubjectAlternativeNames {
192+ ip : vec ! [ IpAddr :: V4 ( Ipv4Addr :: LOCALHOST ) ] ,
193+ dns_name : vec ! [ "my-server.org" . to_string( ) ] ,
194+ } ,
172195 } )
173196 }
174197}
@@ -189,6 +212,8 @@ mod tests {
189212 }
190213
191214 mod json {
215+ use std:: net:: Ipv4Addr ;
216+
192217 use super :: * ;
193218
194219 #[ test]
@@ -226,11 +251,13 @@ mod tests {
226251 "type" : "client" ,
227252 "export_key" : true ,
228253 "ip" : "192.168.1.10" ,
254+ "dns_name" : "my-client.org"
229255 } ,
230256 "server_cert" : {
231257 "type" : "client" ,
232258 "export_key" : true ,
233259 "ip" : "192.168.1.1" ,
260+ "dns_name" : "my-server.org"
234261 }
235262 }
236263 }
@@ -254,6 +281,69 @@ mod tests {
254281
255282 assert_eq ! ( deserialized, certs)
256283 }
284+
285+ #[ test]
286+ fn should_deserialize_client ( ) {
287+ let expected = ClientConfiguration {
288+ export_key : false ,
289+ subject_alternative_names : SubjectAlternativeNames {
290+ ip : vec ! [ IpAddr :: V4 ( Ipv4Addr :: new( 192 , 168 , 1 , 10 ) ) ] ,
291+ dns_name : vec ! [ "my-client.org" . to_string( ) ] ,
292+ } ,
293+ } ;
294+ let json = json ! ( {
295+ "export_key" : false ,
296+ "ip" : "192.168.1.10" ,
297+ "dns_name" : "my-client.org"
298+ } ) ;
299+
300+ let deserialized: ClientConfiguration = serde_json:: from_value ( json) . unwrap ( ) ;
301+
302+ assert_eq ! ( deserialized, expected)
303+ }
304+
305+ #[ test]
306+ fn should_deserialize_multiple_ips_and_dns_snames ( ) {
307+ let expected = ServerConfiguration {
308+ export_key : false ,
309+ subject_alternative_names : SubjectAlternativeNames {
310+ ip : vec ! [
311+ IpAddr :: V4 ( Ipv4Addr :: new( 192 , 168 , 1 , 1 ) ) ,
312+ IpAddr :: V4 ( Ipv4Addr :: new( 192 , 168 , 1 , 2 ) ) ,
313+ ] ,
314+ dns_name : vec ! [ "my-server.org" . to_string( ) , "my-server.com" . to_string( ) ] ,
315+ } ,
316+ } ;
317+ let json = json ! ( {
318+ "export_key" : false ,
319+ "ip" : [ "192.168.1.1" , "192.168.1.2" ] ,
320+ "dns_name" : [ "my-server.org" , "my-server.com" ]
321+ } ) ;
322+
323+ let deserialized: ServerConfiguration = serde_json:: from_value ( json) . unwrap ( ) ;
324+
325+ assert_eq ! ( deserialized, expected)
326+ }
327+
328+ #[ test]
329+ fn should_deserialize_server ( ) {
330+ let expected = ServerConfiguration {
331+ export_key : false ,
332+ subject_alternative_names : SubjectAlternativeNames {
333+ ip : vec ! [ IpAddr :: V4 ( Ipv4Addr :: new( 192 , 168 , 1 , 1 ) ) ] ,
334+ dns_name : vec ! [ "my-server.org" . to_string( ) ] ,
335+ } ,
336+ } ;
337+ let json = json ! ( {
338+ "export_key" : false ,
339+ "ip" : "192.168.1.1" ,
340+ "dns_name" : "my-server.org"
341+ } ) ;
342+
343+ let deserialized: ServerConfiguration = serde_json:: from_value ( json) . unwrap ( ) ;
344+
345+ assert_eq ! ( deserialized, expected)
346+ }
257347 }
258348
259349 mod yaml {
0 commit comments