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 import android.util.Log;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.server.vcn.util.PersistableBundleUtils;
44 
45 import java.net.InetAddress;
46 import java.security.PrivateKey;
47 import java.security.cert.CertificateEncodingException;
48 import java.security.cert.X509Certificate;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.List;
52 import java.util.Objects;
53 import java.util.Set;
54 
55 /**
56  * Abstract utility class to convert IkeSessionParams to/from PersistableBundle.
57  *
58  * @hide
59  */
60 @VisibleForTesting(visibility = Visibility.PRIVATE)
61 public final class IkeSessionParamsUtils {
62     private static final String TAG = IkeSessionParamsUtils.class.getSimpleName();
63 
64     private static final String SERVER_HOST_NAME_KEY = "SERVER_HOST_NAME_KEY";
65     private static final String SA_PROPOSALS_KEY = "SA_PROPOSALS_KEY";
66     private static final String LOCAL_ID_KEY = "LOCAL_ID_KEY";
67     private static final String REMOTE_ID_KEY = "REMOTE_ID_KEY";
68     private static final String LOCAL_AUTH_KEY = "LOCAL_AUTH_KEY";
69     private static final String REMOTE_AUTH_KEY = "REMOTE_AUTH_KEY";
70     private static final String CONFIG_REQUESTS_KEY = "CONFIG_REQUESTS_KEY";
71     private static final String RETRANS_TIMEOUTS_KEY = "RETRANS_TIMEOUTS_KEY";
72     private static final String HARD_LIFETIME_SEC_KEY = "HARD_LIFETIME_SEC_KEY";
73     private static final String SOFT_LIFETIME_SEC_KEY = "SOFT_LIFETIME_SEC_KEY";
74     private static final String DPD_DELAY_SEC_KEY = "DPD_DELAY_SEC_KEY";
75     private static final String NATT_KEEPALIVE_DELAY_SEC_KEY = "NATT_KEEPALIVE_DELAY_SEC_KEY";
76     private static final String IKE_OPTIONS_KEY = "IKE_OPTIONS_KEY";
77     private static final String IP_VERSION_KEY = "IP_VERSION_KEY";
78     private static final String ENCAP_TYPE_KEY = "ENCAP_TYPE_KEY";
79     // TODO: add DSCP_KEY and IS_IKE_FRAGMENT_SUPPORTED_KEY.
80 
81     // TODO: b/243181760 Use the IKE API when they are exposed
82     @VisibleForTesting(visibility = Visibility.PRIVATE)
83     public static final int IKE_OPTION_AUTOMATIC_ADDRESS_FAMILY_SELECTION = 6;
84 
85     @VisibleForTesting(visibility = Visibility.PRIVATE)
86     public static final int IKE_OPTION_AUTOMATIC_NATT_KEEPALIVES = 7;
87 
88     private static final Set<Integer> IKE_OPTIONS = new ArraySet<>();
89 
90     static {
91         IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_ACCEPT_ANY_REMOTE_ID);
92         IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_EAP_ONLY_AUTH);
93         IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_MOBIKE);
94         IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_FORCE_PORT_4500);
95         IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_INITIAL_CONTACT);
96         IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_REKEY_MOBILITY);
97         IKE_OPTIONS.add(IKE_OPTION_AUTOMATIC_ADDRESS_FAMILY_SELECTION);
98         IKE_OPTIONS.add(IKE_OPTION_AUTOMATIC_NATT_KEEPALIVES);
99         IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_AUTOMATIC_KEEPALIVE_ON_OFF);
100     }
101 
102     /**
103      * Check if an IKE option is supported in the IPsec module installed on the device
104      *
105      * <p>This method ensures caller to safely access options that are added between dessert
106      * releases.
107      */
108     @VisibleForTesting(visibility = Visibility.PRIVATE)
isIkeOptionValid(int option)109     public static boolean isIkeOptionValid(int option) {
110         try {
111             new IkeSessionParams.Builder().addIkeOption(option);
112             return true;
113         } catch (IllegalArgumentException e) {
114             Log.d(TAG, "Option not supported; discarding: " + option);
115             return false;
116         }
117     }
118 
119     /** Serializes an IkeSessionParams to a PersistableBundle. */
120     @NonNull
toPersistableBundle(@onNull IkeSessionParams params)121     public static PersistableBundle toPersistableBundle(@NonNull IkeSessionParams params) {
122         if (params.getNetwork() != null || params.getIke3gppExtension() != null) {
123             throw new IllegalStateException(
124                     "Cannot convert a IkeSessionParams with a caller configured network or with"
125                             + " 3GPP extension enabled");
126         }
127 
128         final PersistableBundle result = new PersistableBundle();
129 
130         result.putString(SERVER_HOST_NAME_KEY, params.getServerHostname());
131 
132         final PersistableBundle saProposalBundle =
133                 PersistableBundleUtils.fromList(
134                         params.getSaProposals(), IkeSaProposalUtils::toPersistableBundle);
135         result.putPersistableBundle(SA_PROPOSALS_KEY, saProposalBundle);
136 
137         result.putPersistableBundle(
138                 LOCAL_ID_KEY,
139                 IkeIdentificationUtils.toPersistableBundle(params.getLocalIdentification()));
140         result.putPersistableBundle(
141                 REMOTE_ID_KEY,
142                 IkeIdentificationUtils.toPersistableBundle(params.getRemoteIdentification()));
143 
144         result.putPersistableBundle(
145                 LOCAL_AUTH_KEY, AuthConfigUtils.toPersistableBundle(params.getLocalAuthConfig()));
146         result.putPersistableBundle(
147                 REMOTE_AUTH_KEY, AuthConfigUtils.toPersistableBundle(params.getRemoteAuthConfig()));
148 
149         final List<ConfigRequest> reqList = new ArrayList<>();
150         for (IkeConfigRequest req : params.getConfigurationRequests()) {
151             reqList.add(new ConfigRequest(req));
152         }
153         final PersistableBundle configReqListBundle =
154                 PersistableBundleUtils.fromList(reqList, ConfigRequest::toPersistableBundle);
155         result.putPersistableBundle(CONFIG_REQUESTS_KEY, configReqListBundle);
156 
157         result.putIntArray(RETRANS_TIMEOUTS_KEY, params.getRetransmissionTimeoutsMillis());
158         result.putInt(HARD_LIFETIME_SEC_KEY, params.getHardLifetimeSeconds());
159         result.putInt(SOFT_LIFETIME_SEC_KEY, params.getSoftLifetimeSeconds());
160         result.putInt(DPD_DELAY_SEC_KEY, params.getDpdDelaySeconds());
161         result.putInt(NATT_KEEPALIVE_DELAY_SEC_KEY, params.getNattKeepAliveDelaySeconds());
162         result.putInt(IP_VERSION_KEY, params.getIpVersion());
163         result.putInt(ENCAP_TYPE_KEY, params.getEncapType());
164 
165         final List<Integer> enabledIkeOptions = new ArrayList<>();
166 
167         try {
168             // TODO: b/328844044: Ideally this code should gate the behavior by checking the
169             // com.android.ipsec.flags.enabled_ike_options_api flag but that flag is not accessible
170             // right now. We should either update the code when the flag is accessible or remove the
171             // legacy behavior after VIC SDK finalization
172             enabledIkeOptions.addAll(params.getIkeOptions());
173         } catch (Exception e) {
174             // getIkeOptions throws. It means the API is not available
175             enabledIkeOptions.clear();
176             for (int option : IKE_OPTIONS) {
177                 if (isIkeOptionValid(option) && params.hasIkeOption(option)) {
178                     enabledIkeOptions.add(option);
179                 }
180             }
181         }
182 
183         final int[] optionArray = enabledIkeOptions.stream().mapToInt(i -> i).toArray();
184         result.putIntArray(IKE_OPTIONS_KEY, optionArray);
185 
186         return result;
187     }
188 
189     /** Constructs an IkeSessionParams by deserializing a PersistableBundle. */
190     @NonNull
fromPersistableBundle(@onNull PersistableBundle in)191     public static IkeSessionParams fromPersistableBundle(@NonNull PersistableBundle in) {
192         Objects.requireNonNull(in, "PersistableBundle is null");
193 
194         final IkeSessionParams.Builder builder = new IkeSessionParams.Builder();
195 
196         builder.setServerHostname(in.getString(SERVER_HOST_NAME_KEY));
197 
198         PersistableBundle proposalBundle = in.getPersistableBundle(SA_PROPOSALS_KEY);
199         Objects.requireNonNull(in, "SA Proposals was null");
200         List<IkeSaProposal> saProposals =
201                 PersistableBundleUtils.toList(
202                         proposalBundle, IkeSaProposalUtils::fromPersistableBundle);
203         for (IkeSaProposal proposal : saProposals) {
204             builder.addSaProposal(proposal);
205         }
206 
207         builder.setLocalIdentification(
208                 IkeIdentificationUtils.fromPersistableBundle(
209                         in.getPersistableBundle(LOCAL_ID_KEY)));
210         builder.setRemoteIdentification(
211                 IkeIdentificationUtils.fromPersistableBundle(
212                         in.getPersistableBundle(REMOTE_ID_KEY)));
213 
214         AuthConfigUtils.setBuilderByReadingPersistableBundle(
215                 in.getPersistableBundle(LOCAL_AUTH_KEY),
216                 in.getPersistableBundle(REMOTE_AUTH_KEY),
217                 builder);
218 
219         builder.setRetransmissionTimeoutsMillis(in.getIntArray(RETRANS_TIMEOUTS_KEY));
220         builder.setLifetimeSeconds(
221                 in.getInt(HARD_LIFETIME_SEC_KEY), in.getInt(SOFT_LIFETIME_SEC_KEY));
222         builder.setDpdDelaySeconds(in.getInt(DPD_DELAY_SEC_KEY));
223         builder.setNattKeepAliveDelaySeconds(in.getInt(NATT_KEEPALIVE_DELAY_SEC_KEY));
224         builder.setIpVersion(in.getInt(IP_VERSION_KEY));
225         builder.setEncapType(in.getInt(ENCAP_TYPE_KEY));
226 
227         final PersistableBundle configReqListBundle = in.getPersistableBundle(CONFIG_REQUESTS_KEY);
228         Objects.requireNonNull(configReqListBundle, "Config request list was null");
229         final List<ConfigRequest> reqList =
230                 PersistableBundleUtils.toList(configReqListBundle, ConfigRequest::new);
231         for (ConfigRequest req : reqList) {
232             switch (req.type) {
233                 case ConfigRequest.IPV4_P_CSCF_ADDRESS:
234                     if (req.address == null) {
235                         builder.addPcscfServerRequest(AF_INET);
236                     } else {
237                         builder.addPcscfServerRequest(req.address);
238                     }
239                     break;
240                 case ConfigRequest.IPV6_P_CSCF_ADDRESS:
241                     if (req.address == null) {
242                         builder.addPcscfServerRequest(AF_INET6);
243                     } else {
244                         builder.addPcscfServerRequest(req.address);
245                     }
246                     break;
247                 default:
248                     throw new IllegalArgumentException(
249                             "Unrecognized config request type: " + req.type);
250             }
251         }
252 
253         // Clear IKE Options that are by default enabled
254         for (int option : IKE_OPTIONS) {
255             if (isIkeOptionValid(option)) {
256                 builder.removeIkeOption(option);
257             }
258         }
259 
260         final int[] optionArray = in.getIntArray(IKE_OPTIONS_KEY);
261         for (int option : optionArray) {
262             if (isIkeOptionValid(option)) {
263                 builder.addIkeOption(option);
264             }
265         }
266 
267         return builder.build();
268     }
269 
270     private static final class AuthConfigUtils {
271         private static final int IKE_AUTH_METHOD_PSK = 1;
272         private static final int IKE_AUTH_METHOD_PUB_KEY_SIGNATURE = 2;
273         private static final int IKE_AUTH_METHOD_EAP = 3;
274 
275         private static final String AUTH_METHOD_KEY = "AUTH_METHOD_KEY";
276 
277         @NonNull
toPersistableBundle(@onNull IkeAuthConfig authConfig)278         public static PersistableBundle toPersistableBundle(@NonNull IkeAuthConfig authConfig) {
279             if (authConfig instanceof IkeAuthPskConfig) {
280                 IkeAuthPskConfig config = (IkeAuthPskConfig) authConfig;
281                 return IkeAuthPskConfigUtils.toPersistableBundle(
282                         config, createPersistableBundle(IKE_AUTH_METHOD_PSK));
283             } else if (authConfig instanceof IkeAuthDigitalSignLocalConfig) {
284                 IkeAuthDigitalSignLocalConfig config = (IkeAuthDigitalSignLocalConfig) authConfig;
285                 return IkeAuthDigitalSignConfigUtils.toPersistableBundle(
286                         config, createPersistableBundle(IKE_AUTH_METHOD_PUB_KEY_SIGNATURE));
287             } else if (authConfig instanceof IkeAuthDigitalSignRemoteConfig) {
288                 IkeAuthDigitalSignRemoteConfig config = (IkeAuthDigitalSignRemoteConfig) authConfig;
289                 return IkeAuthDigitalSignConfigUtils.toPersistableBundle(
290                         config, createPersistableBundle(IKE_AUTH_METHOD_PUB_KEY_SIGNATURE));
291             } else if (authConfig instanceof IkeAuthEapConfig) {
292                 IkeAuthEapConfig config = (IkeAuthEapConfig) authConfig;
293                 return IkeAuthEapConfigUtils.toPersistableBundle(
294                         config, createPersistableBundle(IKE_AUTH_METHOD_EAP));
295             } else {
296                 throw new IllegalStateException("Invalid IkeAuthConfig subclass");
297             }
298         }
299 
createPersistableBundle(int type)300         private static PersistableBundle createPersistableBundle(int type) {
301             final PersistableBundle result = new PersistableBundle();
302             result.putInt(AUTH_METHOD_KEY, type);
303             return result;
304         }
305 
setBuilderByReadingPersistableBundle( @onNull PersistableBundle localAuthBundle, @NonNull PersistableBundle remoteAuthBundle, @NonNull IkeSessionParams.Builder builder)306         public static void setBuilderByReadingPersistableBundle(
307                 @NonNull PersistableBundle localAuthBundle,
308                 @NonNull PersistableBundle remoteAuthBundle,
309                 @NonNull IkeSessionParams.Builder builder) {
310             Objects.requireNonNull(localAuthBundle, "localAuthBundle was null");
311             Objects.requireNonNull(remoteAuthBundle, "remoteAuthBundle was null");
312 
313             final int localMethodType = localAuthBundle.getInt(AUTH_METHOD_KEY);
314             final int remoteMethodType = remoteAuthBundle.getInt(AUTH_METHOD_KEY);
315             switch (localMethodType) {
316                 case IKE_AUTH_METHOD_PSK:
317                     if (remoteMethodType != IKE_AUTH_METHOD_PSK) {
318                         throw new IllegalArgumentException(
319                                 "Expect remote auth method to be PSK based, but was "
320                                         + remoteMethodType);
321                     }
322                     IkeAuthPskConfigUtils.setBuilderByReadingPersistableBundle(
323                             localAuthBundle, remoteAuthBundle, builder);
324                     return;
325                 case IKE_AUTH_METHOD_PUB_KEY_SIGNATURE:
326                     if (remoteMethodType != IKE_AUTH_METHOD_PUB_KEY_SIGNATURE) {
327                         throw new IllegalArgumentException(
328                                 "Expect remote auth method to be digital signature based, but was "
329                                         + remoteMethodType);
330                     }
331                     IkeAuthDigitalSignConfigUtils.setBuilderByReadingPersistableBundle(
332                             localAuthBundle, remoteAuthBundle, builder);
333                     return;
334                 case IKE_AUTH_METHOD_EAP:
335                     if (remoteMethodType != IKE_AUTH_METHOD_PUB_KEY_SIGNATURE) {
336                         throw new IllegalArgumentException(
337                                 "When using EAP for local authentication, expect remote auth"
338                                         + " method to be digital signature based, but was "
339                                         + remoteMethodType);
340                     }
341                     IkeAuthEapConfigUtils.setBuilderByReadingPersistableBundle(
342                             localAuthBundle, remoteAuthBundle, builder);
343                     return;
344                 default:
345                     throw new IllegalArgumentException(
346                             "Invalid EAP method type " + localMethodType);
347             }
348         }
349     }
350 
351     private static final class IkeAuthPskConfigUtils {
352         private static final String PSK_KEY = "PSK_KEY";
353 
354         @NonNull
toPersistableBundle( @onNull IkeAuthPskConfig config, @NonNull PersistableBundle result)355         public static PersistableBundle toPersistableBundle(
356                 @NonNull IkeAuthPskConfig config, @NonNull PersistableBundle result) {
357             result.putPersistableBundle(
358                     PSK_KEY, PersistableBundleUtils.fromByteArray(config.getPsk()));
359             return result;
360         }
361 
setBuilderByReadingPersistableBundle( @onNull PersistableBundle localAuthBundle, @NonNull PersistableBundle remoteAuthBundle, @NonNull IkeSessionParams.Builder builder)362         public static void setBuilderByReadingPersistableBundle(
363                 @NonNull PersistableBundle localAuthBundle,
364                 @NonNull PersistableBundle remoteAuthBundle,
365                 @NonNull IkeSessionParams.Builder builder) {
366             Objects.requireNonNull(localAuthBundle, "localAuthBundle was null");
367             Objects.requireNonNull(remoteAuthBundle, "remoteAuthBundle was null");
368 
369             final PersistableBundle localPskBundle = localAuthBundle.getPersistableBundle(PSK_KEY);
370             final PersistableBundle remotePskBundle =
371                     remoteAuthBundle.getPersistableBundle(PSK_KEY);
372             Objects.requireNonNull(localAuthBundle, "Local PSK was null");
373             Objects.requireNonNull(remoteAuthBundle, "Remote PSK was null");
374 
375             final byte[] localPsk = PersistableBundleUtils.toByteArray(localPskBundle);
376             final byte[] remotePsk = PersistableBundleUtils.toByteArray(remotePskBundle);
377             if (!Arrays.equals(localPsk, remotePsk)) {
378                 throw new IllegalArgumentException("Local PSK and remote PSK are different");
379             }
380             builder.setAuthPsk(localPsk);
381         }
382     }
383 
384     private static class IkeAuthDigitalSignConfigUtils {
385         private static final String END_CERT_KEY = "END_CERT_KEY";
386         private static final String INTERMEDIATE_CERTS_KEY = "INTERMEDIATE_CERTS_KEY";
387         private static final String PRIVATE_KEY_KEY = "PRIVATE_KEY_KEY";
388         private static final String TRUST_CERT_KEY = "TRUST_CERT_KEY";
389 
390         @NonNull
toPersistableBundle( @onNull IkeAuthDigitalSignLocalConfig config, @NonNull PersistableBundle result)391         public static PersistableBundle toPersistableBundle(
392                 @NonNull IkeAuthDigitalSignLocalConfig config, @NonNull PersistableBundle result) {
393             try {
394                 result.putPersistableBundle(
395                         END_CERT_KEY,
396                         PersistableBundleUtils.fromByteArray(
397                                 config.getClientEndCertificate().getEncoded()));
398 
399                 final List<X509Certificate> certList = config.getIntermediateCertificates();
400                 final List<byte[]> encodedCertList = new ArrayList<>(certList.size());
401                 for (X509Certificate cert : certList) {
402                     encodedCertList.add(cert.getEncoded());
403                 }
404 
405                 final PersistableBundle certsBundle =
406                         PersistableBundleUtils.fromList(
407                                 encodedCertList, PersistableBundleUtils::fromByteArray);
408                 result.putPersistableBundle(INTERMEDIATE_CERTS_KEY, certsBundle);
409             } catch (CertificateEncodingException e) {
410                 throw new IllegalArgumentException("Fail to encode certificate");
411             }
412 
413             // TODO: b/170670506 Consider putting PrivateKey in Android KeyStore
414             result.putPersistableBundle(
415                     PRIVATE_KEY_KEY,
416                     PersistableBundleUtils.fromByteArray(config.getPrivateKey().getEncoded()));
417             return result;
418         }
419 
420         @NonNull
toPersistableBundle( @onNull IkeAuthDigitalSignRemoteConfig config, @NonNull PersistableBundle result)421         public static PersistableBundle toPersistableBundle(
422                 @NonNull IkeAuthDigitalSignRemoteConfig config, @NonNull PersistableBundle result) {
423             try {
424                 X509Certificate caCert = config.getRemoteCaCert();
425                 if (caCert != null) {
426                     result.putPersistableBundle(
427                             TRUST_CERT_KEY,
428                             PersistableBundleUtils.fromByteArray(caCert.getEncoded()));
429                 }
430             } catch (CertificateEncodingException e) {
431                 throw new IllegalArgumentException("Fail to encode the certificate");
432             }
433 
434             return result;
435         }
436 
setBuilderByReadingPersistableBundle( @onNull PersistableBundle localAuthBundle, @NonNull PersistableBundle remoteAuthBundle, @NonNull IkeSessionParams.Builder builder)437         public static void setBuilderByReadingPersistableBundle(
438                 @NonNull PersistableBundle localAuthBundle,
439                 @NonNull PersistableBundle remoteAuthBundle,
440                 @NonNull IkeSessionParams.Builder builder) {
441             Objects.requireNonNull(localAuthBundle, "localAuthBundle was null");
442             Objects.requireNonNull(remoteAuthBundle, "remoteAuthBundle was null");
443 
444             // Deserialize localAuth
445             final PersistableBundle endCertBundle =
446                     localAuthBundle.getPersistableBundle(END_CERT_KEY);
447             Objects.requireNonNull(endCertBundle, "End cert was null");
448             final byte[] encodedCert = PersistableBundleUtils.toByteArray(endCertBundle);
449             final X509Certificate endCert = CertUtils.certificateFromByteArray(encodedCert);
450 
451             final PersistableBundle certsBundle =
452                     localAuthBundle.getPersistableBundle(INTERMEDIATE_CERTS_KEY);
453             Objects.requireNonNull(certsBundle, "Intermediate certs was null");
454             final List<byte[]> encodedCertList =
455                     PersistableBundleUtils.toList(certsBundle, PersistableBundleUtils::toByteArray);
456             final List<X509Certificate> certList = new ArrayList<>(encodedCertList.size());
457             for (byte[] encoded : encodedCertList) {
458                 certList.add(CertUtils.certificateFromByteArray(encoded));
459             }
460 
461             final PersistableBundle privateKeyBundle =
462                     localAuthBundle.getPersistableBundle(PRIVATE_KEY_KEY);
463             Objects.requireNonNull(privateKeyBundle, "PrivateKey bundle was null");
464             final PrivateKey privateKey =
465                     CertUtils.privateKeyFromByteArray(
466                             PersistableBundleUtils.toByteArray(privateKeyBundle));
467 
468             // Deserialize remoteAuth
469             final PersistableBundle trustCertBundle =
470                     remoteAuthBundle.getPersistableBundle(TRUST_CERT_KEY);
471 
472             X509Certificate caCert = null;
473             if (trustCertBundle != null) {
474                 final byte[] encodedCaCert = PersistableBundleUtils.toByteArray(trustCertBundle);
475                 caCert = CertUtils.certificateFromByteArray(encodedCaCert);
476             }
477 
478             builder.setAuthDigitalSignature(caCert, endCert, certList, privateKey);
479         }
480     }
481 
482     private static final class IkeAuthEapConfigUtils {
483         private static final String EAP_CONFIG_KEY = "EAP_CONFIG_KEY";
484 
485         @NonNull
toPersistableBundle( @onNull IkeAuthEapConfig config, @NonNull PersistableBundle result)486         public static PersistableBundle toPersistableBundle(
487                 @NonNull IkeAuthEapConfig config, @NonNull PersistableBundle result) {
488             result.putPersistableBundle(
489                     EAP_CONFIG_KEY,
490                     EapSessionConfigUtils.toPersistableBundle(config.getEapConfig()));
491             return result;
492         }
493 
setBuilderByReadingPersistableBundle( @onNull PersistableBundle localAuthBundle, @NonNull PersistableBundle remoteAuthBundle, @NonNull IkeSessionParams.Builder builder)494         public static void setBuilderByReadingPersistableBundle(
495                 @NonNull PersistableBundle localAuthBundle,
496                 @NonNull PersistableBundle remoteAuthBundle,
497                 @NonNull IkeSessionParams.Builder builder) {
498             // Deserialize localAuth
499             final PersistableBundle eapBundle =
500                     localAuthBundle.getPersistableBundle(EAP_CONFIG_KEY);
501             Objects.requireNonNull(eapBundle, "EAP Config was null");
502             final EapSessionConfig eapConfig =
503                     EapSessionConfigUtils.fromPersistableBundle(eapBundle);
504 
505             // Deserialize remoteAuth
506             final PersistableBundle trustCertBundle =
507                     remoteAuthBundle.getPersistableBundle(
508                             IkeAuthDigitalSignConfigUtils.TRUST_CERT_KEY);
509 
510             X509Certificate serverCaCert = null;
511             if (trustCertBundle != null) {
512                 final byte[] encodedCaCert = PersistableBundleUtils.toByteArray(trustCertBundle);
513                 serverCaCert = CertUtils.certificateFromByteArray(encodedCaCert);
514             }
515             builder.setAuthEap(serverCaCert, eapConfig);
516         }
517     }
518 
519     private static final class ConfigRequest {
520         private static final int IPV4_P_CSCF_ADDRESS = 1;
521         private static final int IPV6_P_CSCF_ADDRESS = 2;
522 
523         private static final String TYPE_KEY = "type";
524         private static final String ADDRESS_KEY = "address";
525 
526         public final int type;
527 
528         // Null when it is an empty request
529         @Nullable public final InetAddress address;
530 
ConfigRequest(IkeConfigRequest config)531         ConfigRequest(IkeConfigRequest config) {
532             if (config instanceof ConfigRequestIpv4PcscfServer) {
533                 type = IPV4_P_CSCF_ADDRESS;
534                 address = ((ConfigRequestIpv4PcscfServer) config).getAddress();
535             } else if (config instanceof ConfigRequestIpv6PcscfServer) {
536                 type = IPV6_P_CSCF_ADDRESS;
537                 address = ((ConfigRequestIpv6PcscfServer) config).getAddress();
538             } else {
539                 throw new IllegalStateException("Unknown TunnelModeChildConfigRequest");
540             }
541         }
542 
ConfigRequest(PersistableBundle in)543         ConfigRequest(PersistableBundle in) {
544             Objects.requireNonNull(in, "PersistableBundle was null");
545 
546             type = in.getInt(TYPE_KEY);
547 
548             String addressStr = in.getString(ADDRESS_KEY);
549             if (addressStr == null) {
550                 address = null;
551             } else {
552                 address = InetAddresses.parseNumericAddress(addressStr);
553             }
554         }
555 
556         @NonNull
toPersistableBundle()557         public PersistableBundle toPersistableBundle() {
558             final PersistableBundle result = new PersistableBundle();
559 
560             result.putInt(TYPE_KEY, type);
561             if (address != null) {
562                 result.putString(ADDRESS_KEY, address.getHostAddress());
563             }
564 
565             return result;
566         }
567     }
568 }
569