1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net.vcn.persistablebundleutils;
18 
19 import static android.system.OsConstants.AF_INET;
20 import static android.system.OsConstants.AF_INET6;
21 
22 import static com.android.internal.annotations.VisibleForTesting.Visibility;
23 
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.net.InetAddresses;
27 import android.net.eap.EapSessionConfig;
28 import android.net.ipsec.ike.IkeSaProposal;
29 import android.net.ipsec.ike.IkeSessionParams;
30 import android.net.ipsec.ike.IkeSessionParams.ConfigRequestIpv4PcscfServer;
31 import android.net.ipsec.ike.IkeSessionParams.ConfigRequestIpv6PcscfServer;
32 import android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig;
33 import android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignLocalConfig;
34 import android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignRemoteConfig;
35 import android.net.ipsec.ike.IkeSessionParams.IkeAuthEapConfig;
36 import android.net.ipsec.ike.IkeSessionParams.IkeAuthPskConfig;
37 import android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest;
38 import android.os.PersistableBundle;
39 import android.util.ArraySet;
40 
41 import com.android.internal.annotations.VisibleForTesting;
42 import com.android.server.vcn.util.PersistableBundleUtils;
43 
44 import java.net.InetAddress;
45 import java.security.PrivateKey;
46 import java.security.cert.CertificateEncodingException;
47 import java.security.cert.X509Certificate;
48 import java.util.ArrayList;
49 import java.util.Arrays;
50 import java.util.List;
51 import java.util.Objects;
52 import java.util.Set;
53 
54 /**
55  * Abstract utility class to convert IkeSessionParams to/from PersistableBundle.
56  *
57  * @hide
58  */
59 @VisibleForTesting(visibility = Visibility.PRIVATE)
60 public final class IkeSessionParamsUtils {
61     private static final String SERVER_HOST_NAME_KEY = "SERVER_HOST_NAME_KEY";
62     private static final String SA_PROPOSALS_KEY = "SA_PROPOSALS_KEY";
63     private static final String LOCAL_ID_KEY = "LOCAL_ID_KEY";
64     private static final String REMOTE_ID_KEY = "REMOTE_ID_KEY";
65     private static final String LOCAL_AUTH_KEY = "LOCAL_AUTH_KEY";
66     private static final String REMOTE_AUTH_KEY = "REMOTE_AUTH_KEY";
67     private static final String CONFIG_REQUESTS_KEY = "CONFIG_REQUESTS_KEY";
68     private static final String RETRANS_TIMEOUTS_KEY = "RETRANS_TIMEOUTS_KEY";
69     private static final String HARD_LIFETIME_SEC_KEY = "HARD_LIFETIME_SEC_KEY";
70     private static final String SOFT_LIFETIME_SEC_KEY = "SOFT_LIFETIME_SEC_KEY";
71     private static final String DPD_DELAY_SEC_KEY = "DPD_DELAY_SEC_KEY";
72     private static final String NATT_KEEPALIVE_DELAY_SEC_KEY = "NATT_KEEPALIVE_DELAY_SEC_KEY";
73     private static final String IKE_OPTIONS_KEY = "IKE_OPTIONS_KEY";
74 
75     private static final Set<Integer> IKE_OPTIONS = new ArraySet<>();
76 
77     static {
78         IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_ACCEPT_ANY_REMOTE_ID);
79         IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_EAP_ONLY_AUTH);
80         IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_MOBIKE);
81         IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_FORCE_PORT_4500);
82     }
83 
84     /** Serializes an IkeSessionParams to a PersistableBundle. */
85     @NonNull
toPersistableBundle(@onNull IkeSessionParams params)86     public static PersistableBundle toPersistableBundle(@NonNull IkeSessionParams params) {
87         if (params.getNetwork() != null || params.getIke3gppExtension() != null) {
88             throw new IllegalStateException(
89                     "Cannot convert a IkeSessionParams with a caller configured network or with"
90                             + " 3GPP extension enabled");
91         }
92 
93         final PersistableBundle result = new PersistableBundle();
94 
95         result.putString(SERVER_HOST_NAME_KEY, params.getServerHostname());
96 
97         final PersistableBundle saProposalBundle =
98                 PersistableBundleUtils.fromList(
99                         params.getSaProposals(), IkeSaProposalUtils::toPersistableBundle);
100         result.putPersistableBundle(SA_PROPOSALS_KEY, saProposalBundle);
101 
102         result.putPersistableBundle(
103                 LOCAL_ID_KEY,
104                 IkeIdentificationUtils.toPersistableBundle(params.getLocalIdentification()));
105         result.putPersistableBundle(
106                 REMOTE_ID_KEY,
107                 IkeIdentificationUtils.toPersistableBundle(params.getRemoteIdentification()));
108 
109         result.putPersistableBundle(
110                 LOCAL_AUTH_KEY, AuthConfigUtils.toPersistableBundle(params.getLocalAuthConfig()));
111         result.putPersistableBundle(
112                 REMOTE_AUTH_KEY, AuthConfigUtils.toPersistableBundle(params.getRemoteAuthConfig()));
113 
114         final List<ConfigRequest> reqList = new ArrayList<>();
115         for (IkeConfigRequest req : params.getConfigurationRequests()) {
116             reqList.add(new ConfigRequest(req));
117         }
118         final PersistableBundle configReqListBundle =
119                 PersistableBundleUtils.fromList(reqList, ConfigRequest::toPersistableBundle);
120         result.putPersistableBundle(CONFIG_REQUESTS_KEY, configReqListBundle);
121 
122         result.putIntArray(RETRANS_TIMEOUTS_KEY, params.getRetransmissionTimeoutsMillis());
123         result.putInt(HARD_LIFETIME_SEC_KEY, params.getHardLifetimeSeconds());
124         result.putInt(SOFT_LIFETIME_SEC_KEY, params.getSoftLifetimeSeconds());
125         result.putInt(DPD_DELAY_SEC_KEY, params.getDpdDelaySeconds());
126         result.putInt(NATT_KEEPALIVE_DELAY_SEC_KEY, params.getNattKeepAliveDelaySeconds());
127 
128         // TODO: b/185941731 Make sure IkeSessionParamsUtils is automatically updated when a new
129         // IKE_OPTION is defined in IKE module and added in the IkeSessionParams
130         final List<Integer> enabledIkeOptions = new ArrayList<>();
131         for (int option : IKE_OPTIONS) {
132             if (params.hasIkeOption(option)) {
133                 enabledIkeOptions.add(option);
134             }
135         }
136 
137         final int[] optionArray = enabledIkeOptions.stream().mapToInt(i -> i).toArray();
138         result.putIntArray(IKE_OPTIONS_KEY, optionArray);
139 
140         return result;
141     }
142 
143     /** Constructs an IkeSessionParams by deserializing a PersistableBundle. */
144     @NonNull
fromPersistableBundle(@onNull PersistableBundle in)145     public static IkeSessionParams fromPersistableBundle(@NonNull PersistableBundle in) {
146         Objects.requireNonNull(in, "PersistableBundle is null");
147 
148         final IkeSessionParams.Builder builder = new IkeSessionParams.Builder();
149 
150         builder.setServerHostname(in.getString(SERVER_HOST_NAME_KEY));
151 
152         PersistableBundle proposalBundle = in.getPersistableBundle(SA_PROPOSALS_KEY);
153         Objects.requireNonNull(in, "SA Proposals was null");
154         List<IkeSaProposal> saProposals =
155                 PersistableBundleUtils.toList(
156                         proposalBundle, IkeSaProposalUtils::fromPersistableBundle);
157         for (IkeSaProposal proposal : saProposals) {
158             builder.addSaProposal(proposal);
159         }
160 
161         builder.setLocalIdentification(
162                 IkeIdentificationUtils.fromPersistableBundle(
163                         in.getPersistableBundle(LOCAL_ID_KEY)));
164         builder.setRemoteIdentification(
165                 IkeIdentificationUtils.fromPersistableBundle(
166                         in.getPersistableBundle(REMOTE_ID_KEY)));
167 
168         AuthConfigUtils.setBuilderByReadingPersistableBundle(
169                 in.getPersistableBundle(LOCAL_AUTH_KEY),
170                 in.getPersistableBundle(REMOTE_AUTH_KEY),
171                 builder);
172 
173         builder.setRetransmissionTimeoutsMillis(in.getIntArray(RETRANS_TIMEOUTS_KEY));
174         builder.setLifetimeSeconds(
175                 in.getInt(HARD_LIFETIME_SEC_KEY), in.getInt(SOFT_LIFETIME_SEC_KEY));
176         builder.setDpdDelaySeconds(in.getInt(DPD_DELAY_SEC_KEY));
177         builder.setNattKeepAliveDelaySeconds(in.getInt(NATT_KEEPALIVE_DELAY_SEC_KEY));
178 
179         final PersistableBundle configReqListBundle = in.getPersistableBundle(CONFIG_REQUESTS_KEY);
180         Objects.requireNonNull(configReqListBundle, "Config request list was null");
181         final List<ConfigRequest> reqList =
182                 PersistableBundleUtils.toList(configReqListBundle, ConfigRequest::new);
183         for (ConfigRequest req : reqList) {
184             switch (req.type) {
185                 case ConfigRequest.IPV4_P_CSCF_ADDRESS:
186                     if (req.address == null) {
187                         builder.addPcscfServerRequest(AF_INET);
188                     } else {
189                         builder.addPcscfServerRequest(req.address);
190                     }
191                     break;
192                 case ConfigRequest.IPV6_P_CSCF_ADDRESS:
193                     if (req.address == null) {
194                         builder.addPcscfServerRequest(AF_INET6);
195                     } else {
196                         builder.addPcscfServerRequest(req.address);
197                     }
198                     break;
199                 default:
200                     throw new IllegalArgumentException(
201                             "Unrecognized config request type: " + req.type);
202             }
203         }
204 
205         // Clear IKE Options that are by default enabled
206         for (int option : IKE_OPTIONS) {
207             builder.removeIkeOption(option);
208         }
209 
210         final int[] optionArray = in.getIntArray(IKE_OPTIONS_KEY);
211         for (int option : optionArray) {
212             builder.addIkeOption(option);
213         }
214 
215         return builder.build();
216     }
217 
218     private static final class AuthConfigUtils {
219         private static final int IKE_AUTH_METHOD_PSK = 1;
220         private static final int IKE_AUTH_METHOD_PUB_KEY_SIGNATURE = 2;
221         private static final int IKE_AUTH_METHOD_EAP = 3;
222 
223         private static final String AUTH_METHOD_KEY = "AUTH_METHOD_KEY";
224 
225         @NonNull
toPersistableBundle(@onNull IkeAuthConfig authConfig)226         public static PersistableBundle toPersistableBundle(@NonNull IkeAuthConfig authConfig) {
227             if (authConfig instanceof IkeAuthPskConfig) {
228                 IkeAuthPskConfig config = (IkeAuthPskConfig) authConfig;
229                 return IkeAuthPskConfigUtils.toPersistableBundle(
230                         config, createPersistableBundle(IKE_AUTH_METHOD_PSK));
231             } else if (authConfig instanceof IkeAuthDigitalSignLocalConfig) {
232                 IkeAuthDigitalSignLocalConfig config = (IkeAuthDigitalSignLocalConfig) authConfig;
233                 return IkeAuthDigitalSignConfigUtils.toPersistableBundle(
234                         config, createPersistableBundle(IKE_AUTH_METHOD_PUB_KEY_SIGNATURE));
235             } else if (authConfig instanceof IkeAuthDigitalSignRemoteConfig) {
236                 IkeAuthDigitalSignRemoteConfig config = (IkeAuthDigitalSignRemoteConfig) authConfig;
237                 return IkeAuthDigitalSignConfigUtils.toPersistableBundle(
238                         config, createPersistableBundle(IKE_AUTH_METHOD_PUB_KEY_SIGNATURE));
239             } else if (authConfig instanceof IkeAuthEapConfig) {
240                 IkeAuthEapConfig config = (IkeAuthEapConfig) authConfig;
241                 return IkeAuthEapConfigUtils.toPersistableBundle(
242                         config, createPersistableBundle(IKE_AUTH_METHOD_EAP));
243             } else {
244                 throw new IllegalStateException("Invalid IkeAuthConfig subclass");
245             }
246         }
247 
createPersistableBundle(int type)248         private static PersistableBundle createPersistableBundle(int type) {
249             final PersistableBundle result = new PersistableBundle();
250             result.putInt(AUTH_METHOD_KEY, type);
251             return result;
252         }
253 
setBuilderByReadingPersistableBundle( @onNull PersistableBundle localAuthBundle, @NonNull PersistableBundle remoteAuthBundle, @NonNull IkeSessionParams.Builder builder)254         public static void setBuilderByReadingPersistableBundle(
255                 @NonNull PersistableBundle localAuthBundle,
256                 @NonNull PersistableBundle remoteAuthBundle,
257                 @NonNull IkeSessionParams.Builder builder) {
258             Objects.requireNonNull(localAuthBundle, "localAuthBundle was null");
259             Objects.requireNonNull(remoteAuthBundle, "remoteAuthBundle was null");
260 
261             final int localMethodType = localAuthBundle.getInt(AUTH_METHOD_KEY);
262             final int remoteMethodType = remoteAuthBundle.getInt(AUTH_METHOD_KEY);
263             switch (localMethodType) {
264                 case IKE_AUTH_METHOD_PSK:
265                     if (remoteMethodType != IKE_AUTH_METHOD_PSK) {
266                         throw new IllegalArgumentException(
267                                 "Expect remote auth method to be PSK based, but was "
268                                         + remoteMethodType);
269                     }
270                     IkeAuthPskConfigUtils.setBuilderByReadingPersistableBundle(
271                             localAuthBundle, remoteAuthBundle, builder);
272                     return;
273                 case IKE_AUTH_METHOD_PUB_KEY_SIGNATURE:
274                     if (remoteMethodType != IKE_AUTH_METHOD_PUB_KEY_SIGNATURE) {
275                         throw new IllegalArgumentException(
276                                 "Expect remote auth method to be digital signature based, but was "
277                                         + remoteMethodType);
278                     }
279                     IkeAuthDigitalSignConfigUtils.setBuilderByReadingPersistableBundle(
280                             localAuthBundle, remoteAuthBundle, builder);
281                     return;
282                 case IKE_AUTH_METHOD_EAP:
283                     if (remoteMethodType != IKE_AUTH_METHOD_PUB_KEY_SIGNATURE) {
284                         throw new IllegalArgumentException(
285                                 "When using EAP for local authentication, expect remote auth"
286                                         + " method to be digital signature based, but was "
287                                         + remoteMethodType);
288                     }
289                     IkeAuthEapConfigUtils.setBuilderByReadingPersistableBundle(
290                             localAuthBundle, remoteAuthBundle, builder);
291                     return;
292                 default:
293                     throw new IllegalArgumentException(
294                             "Invalid EAP method type " + localMethodType);
295             }
296         }
297     }
298 
299     private static final class IkeAuthPskConfigUtils {
300         private static final String PSK_KEY = "PSK_KEY";
301 
302         @NonNull
toPersistableBundle( @onNull IkeAuthPskConfig config, @NonNull PersistableBundle result)303         public static PersistableBundle toPersistableBundle(
304                 @NonNull IkeAuthPskConfig config, @NonNull PersistableBundle result) {
305             result.putPersistableBundle(
306                     PSK_KEY, PersistableBundleUtils.fromByteArray(config.getPsk()));
307             return result;
308         }
309 
setBuilderByReadingPersistableBundle( @onNull PersistableBundle localAuthBundle, @NonNull PersistableBundle remoteAuthBundle, @NonNull IkeSessionParams.Builder builder)310         public static void setBuilderByReadingPersistableBundle(
311                 @NonNull PersistableBundle localAuthBundle,
312                 @NonNull PersistableBundle remoteAuthBundle,
313                 @NonNull IkeSessionParams.Builder builder) {
314             Objects.requireNonNull(localAuthBundle, "localAuthBundle was null");
315             Objects.requireNonNull(remoteAuthBundle, "remoteAuthBundle was null");
316 
317             final PersistableBundle localPskBundle = localAuthBundle.getPersistableBundle(PSK_KEY);
318             final PersistableBundle remotePskBundle =
319                     remoteAuthBundle.getPersistableBundle(PSK_KEY);
320             Objects.requireNonNull(localAuthBundle, "Local PSK was null");
321             Objects.requireNonNull(remoteAuthBundle, "Remote PSK was null");
322 
323             final byte[] localPsk = PersistableBundleUtils.toByteArray(localPskBundle);
324             final byte[] remotePsk = PersistableBundleUtils.toByteArray(remotePskBundle);
325             if (!Arrays.equals(localPsk, remotePsk)) {
326                 throw new IllegalArgumentException("Local PSK and remote PSK are different");
327             }
328             builder.setAuthPsk(localPsk);
329         }
330     }
331 
332     private static class IkeAuthDigitalSignConfigUtils {
333         private static final String END_CERT_KEY = "END_CERT_KEY";
334         private static final String INTERMEDIATE_CERTS_KEY = "INTERMEDIATE_CERTS_KEY";
335         private static final String PRIVATE_KEY_KEY = "PRIVATE_KEY_KEY";
336         private static final String TRUST_CERT_KEY = "TRUST_CERT_KEY";
337 
338         @NonNull
toPersistableBundle( @onNull IkeAuthDigitalSignLocalConfig config, @NonNull PersistableBundle result)339         public static PersistableBundle toPersistableBundle(
340                 @NonNull IkeAuthDigitalSignLocalConfig config, @NonNull PersistableBundle result) {
341             try {
342                 result.putPersistableBundle(
343                         END_CERT_KEY,
344                         PersistableBundleUtils.fromByteArray(
345                                 config.getClientEndCertificate().getEncoded()));
346 
347                 final List<X509Certificate> certList = config.getIntermediateCertificates();
348                 final List<byte[]> encodedCertList = new ArrayList<>(certList.size());
349                 for (X509Certificate cert : certList) {
350                     encodedCertList.add(cert.getEncoded());
351                 }
352 
353                 final PersistableBundle certsBundle =
354                         PersistableBundleUtils.fromList(
355                                 encodedCertList, PersistableBundleUtils::fromByteArray);
356                 result.putPersistableBundle(INTERMEDIATE_CERTS_KEY, certsBundle);
357             } catch (CertificateEncodingException e) {
358                 throw new IllegalArgumentException("Fail to encode certificate");
359             }
360 
361             // TODO: b/170670506 Consider putting PrivateKey in Android KeyStore
362             result.putPersistableBundle(
363                     PRIVATE_KEY_KEY,
364                     PersistableBundleUtils.fromByteArray(config.getPrivateKey().getEncoded()));
365             return result;
366         }
367 
368         @NonNull
toPersistableBundle( @onNull IkeAuthDigitalSignRemoteConfig config, @NonNull PersistableBundle result)369         public static PersistableBundle toPersistableBundle(
370                 @NonNull IkeAuthDigitalSignRemoteConfig config, @NonNull PersistableBundle result) {
371             try {
372                 X509Certificate caCert = config.getRemoteCaCert();
373                 if (caCert != null) {
374                     result.putPersistableBundle(
375                             TRUST_CERT_KEY,
376                             PersistableBundleUtils.fromByteArray(caCert.getEncoded()));
377                 }
378             } catch (CertificateEncodingException e) {
379                 throw new IllegalArgumentException("Fail to encode the certificate");
380             }
381 
382             return result;
383         }
384 
setBuilderByReadingPersistableBundle( @onNull PersistableBundle localAuthBundle, @NonNull PersistableBundle remoteAuthBundle, @NonNull IkeSessionParams.Builder builder)385         public static void setBuilderByReadingPersistableBundle(
386                 @NonNull PersistableBundle localAuthBundle,
387                 @NonNull PersistableBundle remoteAuthBundle,
388                 @NonNull IkeSessionParams.Builder builder) {
389             Objects.requireNonNull(localAuthBundle, "localAuthBundle was null");
390             Objects.requireNonNull(remoteAuthBundle, "remoteAuthBundle was null");
391 
392             // Deserialize localAuth
393             final PersistableBundle endCertBundle =
394                     localAuthBundle.getPersistableBundle(END_CERT_KEY);
395             Objects.requireNonNull(endCertBundle, "End cert was null");
396             final byte[] encodedCert = PersistableBundleUtils.toByteArray(endCertBundle);
397             final X509Certificate endCert = CertUtils.certificateFromByteArray(encodedCert);
398 
399             final PersistableBundle certsBundle =
400                     localAuthBundle.getPersistableBundle(INTERMEDIATE_CERTS_KEY);
401             Objects.requireNonNull(certsBundle, "Intermediate certs was null");
402             final List<byte[]> encodedCertList =
403                     PersistableBundleUtils.toList(certsBundle, PersistableBundleUtils::toByteArray);
404             final List<X509Certificate> certList = new ArrayList<>(encodedCertList.size());
405             for (byte[] encoded : encodedCertList) {
406                 certList.add(CertUtils.certificateFromByteArray(encoded));
407             }
408 
409             final PersistableBundle privateKeyBundle =
410                     localAuthBundle.getPersistableBundle(PRIVATE_KEY_KEY);
411             Objects.requireNonNull(privateKeyBundle, "PrivateKey bundle was null");
412             final PrivateKey privateKey =
413                     CertUtils.privateKeyFromByteArray(
414                             PersistableBundleUtils.toByteArray(privateKeyBundle));
415 
416             // Deserialize remoteAuth
417             final PersistableBundle trustCertBundle =
418                     remoteAuthBundle.getPersistableBundle(TRUST_CERT_KEY);
419 
420             X509Certificate caCert = null;
421             if (trustCertBundle != null) {
422                 final byte[] encodedCaCert = PersistableBundleUtils.toByteArray(trustCertBundle);
423                 caCert = CertUtils.certificateFromByteArray(encodedCaCert);
424             }
425 
426             builder.setAuthDigitalSignature(caCert, endCert, certList, privateKey);
427         }
428     }
429 
430     private static final class IkeAuthEapConfigUtils {
431         private static final String EAP_CONFIG_KEY = "EAP_CONFIG_KEY";
432 
433         @NonNull
toPersistableBundle( @onNull IkeAuthEapConfig config, @NonNull PersistableBundle result)434         public static PersistableBundle toPersistableBundle(
435                 @NonNull IkeAuthEapConfig config, @NonNull PersistableBundle result) {
436             result.putPersistableBundle(
437                     EAP_CONFIG_KEY,
438                     EapSessionConfigUtils.toPersistableBundle(config.getEapConfig()));
439             return result;
440         }
441 
setBuilderByReadingPersistableBundle( @onNull PersistableBundle localAuthBundle, @NonNull PersistableBundle remoteAuthBundle, @NonNull IkeSessionParams.Builder builder)442         public static void setBuilderByReadingPersistableBundle(
443                 @NonNull PersistableBundle localAuthBundle,
444                 @NonNull PersistableBundle remoteAuthBundle,
445                 @NonNull IkeSessionParams.Builder builder) {
446             // Deserialize localAuth
447             final PersistableBundle eapBundle =
448                     localAuthBundle.getPersistableBundle(EAP_CONFIG_KEY);
449             Objects.requireNonNull(eapBundle, "EAP Config was null");
450             final EapSessionConfig eapConfig =
451                     EapSessionConfigUtils.fromPersistableBundle(eapBundle);
452 
453             // Deserialize remoteAuth
454             final PersistableBundle trustCertBundle =
455                     remoteAuthBundle.getPersistableBundle(
456                             IkeAuthDigitalSignConfigUtils.TRUST_CERT_KEY);
457 
458             X509Certificate serverCaCert = null;
459             if (trustCertBundle != null) {
460                 final byte[] encodedCaCert = PersistableBundleUtils.toByteArray(trustCertBundle);
461                 serverCaCert = CertUtils.certificateFromByteArray(encodedCaCert);
462             }
463             builder.setAuthEap(serverCaCert, eapConfig);
464         }
465     }
466 
467     private static final class ConfigRequest {
468         private static final int IPV4_P_CSCF_ADDRESS = 1;
469         private static final int IPV6_P_CSCF_ADDRESS = 2;
470 
471         private static final String TYPE_KEY = "type";
472         private static final String ADDRESS_KEY = "address";
473 
474         public final int type;
475 
476         // Null when it is an empty request
477         @Nullable public final InetAddress address;
478 
ConfigRequest(IkeConfigRequest config)479         ConfigRequest(IkeConfigRequest config) {
480             if (config instanceof ConfigRequestIpv4PcscfServer) {
481                 type = IPV4_P_CSCF_ADDRESS;
482                 address = ((ConfigRequestIpv4PcscfServer) config).getAddress();
483             } else if (config instanceof ConfigRequestIpv6PcscfServer) {
484                 type = IPV6_P_CSCF_ADDRESS;
485                 address = ((ConfigRequestIpv6PcscfServer) config).getAddress();
486             } else {
487                 throw new IllegalStateException("Unknown TunnelModeChildConfigRequest");
488             }
489         }
490 
ConfigRequest(PersistableBundle in)491         ConfigRequest(PersistableBundle in) {
492             Objects.requireNonNull(in, "PersistableBundle was null");
493 
494             type = in.getInt(TYPE_KEY);
495 
496             String addressStr = in.getString(ADDRESS_KEY);
497             if (addressStr == null) {
498                 address = null;
499             } else {
500                 address = InetAddresses.parseNumericAddress(addressStr);
501             }
502         }
503 
504         @NonNull
toPersistableBundle()505         public PersistableBundle toPersistableBundle() {
506             final PersistableBundle result = new PersistableBundle();
507 
508             result.putInt(TYPE_KEY, type);
509             if (address != null) {
510                 result.putString(ADDRESS_KEY, address.getHostAddress());
511             }
512 
513             return result;
514         }
515     }
516 }
517