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         // TODO: b/185941731 Make sure IkeSessionParamsUtils is automatically updated when a new
166         // IKE_OPTION is defined in IKE module and added in the IkeSessionParams
167         final List<Integer> enabledIkeOptions = new ArrayList<>();
168         for (int option : IKE_OPTIONS) {
169             if (isIkeOptionValid(option) && params.hasIkeOption(option)) {
170                 enabledIkeOptions.add(option);
171             }
172         }
173 
174         final int[] optionArray = enabledIkeOptions.stream().mapToInt(i -> i).toArray();
175         result.putIntArray(IKE_OPTIONS_KEY, optionArray);
176 
177         return result;
178     }
179 
180     /** Constructs an IkeSessionParams by deserializing a PersistableBundle. */
181     @NonNull
fromPersistableBundle(@onNull PersistableBundle in)182     public static IkeSessionParams fromPersistableBundle(@NonNull PersistableBundle in) {
183         Objects.requireNonNull(in, "PersistableBundle is null");
184 
185         final IkeSessionParams.Builder builder = new IkeSessionParams.Builder();
186 
187         builder.setServerHostname(in.getString(SERVER_HOST_NAME_KEY));
188 
189         PersistableBundle proposalBundle = in.getPersistableBundle(SA_PROPOSALS_KEY);
190         Objects.requireNonNull(in, "SA Proposals was null");
191         List<IkeSaProposal> saProposals =
192                 PersistableBundleUtils.toList(
193                         proposalBundle, IkeSaProposalUtils::fromPersistableBundle);
194         for (IkeSaProposal proposal : saProposals) {
195             builder.addSaProposal(proposal);
196         }
197 
198         builder.setLocalIdentification(
199                 IkeIdentificationUtils.fromPersistableBundle(
200                         in.getPersistableBundle(LOCAL_ID_KEY)));
201         builder.setRemoteIdentification(
202                 IkeIdentificationUtils.fromPersistableBundle(
203                         in.getPersistableBundle(REMOTE_ID_KEY)));
204 
205         AuthConfigUtils.setBuilderByReadingPersistableBundle(
206                 in.getPersistableBundle(LOCAL_AUTH_KEY),
207                 in.getPersistableBundle(REMOTE_AUTH_KEY),
208                 builder);
209 
210         builder.setRetransmissionTimeoutsMillis(in.getIntArray(RETRANS_TIMEOUTS_KEY));
211         builder.setLifetimeSeconds(
212                 in.getInt(HARD_LIFETIME_SEC_KEY), in.getInt(SOFT_LIFETIME_SEC_KEY));
213         builder.setDpdDelaySeconds(in.getInt(DPD_DELAY_SEC_KEY));
214         builder.setNattKeepAliveDelaySeconds(in.getInt(NATT_KEEPALIVE_DELAY_SEC_KEY));
215         builder.setIpVersion(in.getInt(IP_VERSION_KEY));
216         builder.setEncapType(in.getInt(ENCAP_TYPE_KEY));
217 
218         final PersistableBundle configReqListBundle = in.getPersistableBundle(CONFIG_REQUESTS_KEY);
219         Objects.requireNonNull(configReqListBundle, "Config request list was null");
220         final List<ConfigRequest> reqList =
221                 PersistableBundleUtils.toList(configReqListBundle, ConfigRequest::new);
222         for (ConfigRequest req : reqList) {
223             switch (req.type) {
224                 case ConfigRequest.IPV4_P_CSCF_ADDRESS:
225                     if (req.address == null) {
226                         builder.addPcscfServerRequest(AF_INET);
227                     } else {
228                         builder.addPcscfServerRequest(req.address);
229                     }
230                     break;
231                 case ConfigRequest.IPV6_P_CSCF_ADDRESS:
232                     if (req.address == null) {
233                         builder.addPcscfServerRequest(AF_INET6);
234                     } else {
235                         builder.addPcscfServerRequest(req.address);
236                     }
237                     break;
238                 default:
239                     throw new IllegalArgumentException(
240                             "Unrecognized config request type: " + req.type);
241             }
242         }
243 
244         // Clear IKE Options that are by default enabled
245         for (int option : IKE_OPTIONS) {
246             if (isIkeOptionValid(option)) {
247                 builder.removeIkeOption(option);
248             }
249         }
250 
251         final int[] optionArray = in.getIntArray(IKE_OPTIONS_KEY);
252         for (int option : optionArray) {
253             if (isIkeOptionValid(option)) {
254                 builder.addIkeOption(option);
255             }
256         }
257 
258         return builder.build();
259     }
260 
261     private static final class AuthConfigUtils {
262         private static final int IKE_AUTH_METHOD_PSK = 1;
263         private static final int IKE_AUTH_METHOD_PUB_KEY_SIGNATURE = 2;
264         private static final int IKE_AUTH_METHOD_EAP = 3;
265 
266         private static final String AUTH_METHOD_KEY = "AUTH_METHOD_KEY";
267 
268         @NonNull
toPersistableBundle(@onNull IkeAuthConfig authConfig)269         public static PersistableBundle toPersistableBundle(@NonNull IkeAuthConfig authConfig) {
270             if (authConfig instanceof IkeAuthPskConfig) {
271                 IkeAuthPskConfig config = (IkeAuthPskConfig) authConfig;
272                 return IkeAuthPskConfigUtils.toPersistableBundle(
273                         config, createPersistableBundle(IKE_AUTH_METHOD_PSK));
274             } else if (authConfig instanceof IkeAuthDigitalSignLocalConfig) {
275                 IkeAuthDigitalSignLocalConfig config = (IkeAuthDigitalSignLocalConfig) authConfig;
276                 return IkeAuthDigitalSignConfigUtils.toPersistableBundle(
277                         config, createPersistableBundle(IKE_AUTH_METHOD_PUB_KEY_SIGNATURE));
278             } else if (authConfig instanceof IkeAuthDigitalSignRemoteConfig) {
279                 IkeAuthDigitalSignRemoteConfig config = (IkeAuthDigitalSignRemoteConfig) authConfig;
280                 return IkeAuthDigitalSignConfigUtils.toPersistableBundle(
281                         config, createPersistableBundle(IKE_AUTH_METHOD_PUB_KEY_SIGNATURE));
282             } else if (authConfig instanceof IkeAuthEapConfig) {
283                 IkeAuthEapConfig config = (IkeAuthEapConfig) authConfig;
284                 return IkeAuthEapConfigUtils.toPersistableBundle(
285                         config, createPersistableBundle(IKE_AUTH_METHOD_EAP));
286             } else {
287                 throw new IllegalStateException("Invalid IkeAuthConfig subclass");
288             }
289         }
290 
createPersistableBundle(int type)291         private static PersistableBundle createPersistableBundle(int type) {
292             final PersistableBundle result = new PersistableBundle();
293             result.putInt(AUTH_METHOD_KEY, type);
294             return result;
295         }
296 
setBuilderByReadingPersistableBundle( @onNull PersistableBundle localAuthBundle, @NonNull PersistableBundle remoteAuthBundle, @NonNull IkeSessionParams.Builder builder)297         public static void setBuilderByReadingPersistableBundle(
298                 @NonNull PersistableBundle localAuthBundle,
299                 @NonNull PersistableBundle remoteAuthBundle,
300                 @NonNull IkeSessionParams.Builder builder) {
301             Objects.requireNonNull(localAuthBundle, "localAuthBundle was null");
302             Objects.requireNonNull(remoteAuthBundle, "remoteAuthBundle was null");
303 
304             final int localMethodType = localAuthBundle.getInt(AUTH_METHOD_KEY);
305             final int remoteMethodType = remoteAuthBundle.getInt(AUTH_METHOD_KEY);
306             switch (localMethodType) {
307                 case IKE_AUTH_METHOD_PSK:
308                     if (remoteMethodType != IKE_AUTH_METHOD_PSK) {
309                         throw new IllegalArgumentException(
310                                 "Expect remote auth method to be PSK based, but was "
311                                         + remoteMethodType);
312                     }
313                     IkeAuthPskConfigUtils.setBuilderByReadingPersistableBundle(
314                             localAuthBundle, remoteAuthBundle, builder);
315                     return;
316                 case IKE_AUTH_METHOD_PUB_KEY_SIGNATURE:
317                     if (remoteMethodType != IKE_AUTH_METHOD_PUB_KEY_SIGNATURE) {
318                         throw new IllegalArgumentException(
319                                 "Expect remote auth method to be digital signature based, but was "
320                                         + remoteMethodType);
321                     }
322                     IkeAuthDigitalSignConfigUtils.setBuilderByReadingPersistableBundle(
323                             localAuthBundle, remoteAuthBundle, builder);
324                     return;
325                 case IKE_AUTH_METHOD_EAP:
326                     if (remoteMethodType != IKE_AUTH_METHOD_PUB_KEY_SIGNATURE) {
327                         throw new IllegalArgumentException(
328                                 "When using EAP for local authentication, expect remote auth"
329                                         + " method to be digital signature based, but was "
330                                         + remoteMethodType);
331                     }
332                     IkeAuthEapConfigUtils.setBuilderByReadingPersistableBundle(
333                             localAuthBundle, remoteAuthBundle, builder);
334                     return;
335                 default:
336                     throw new IllegalArgumentException(
337                             "Invalid EAP method type " + localMethodType);
338             }
339         }
340     }
341 
342     private static final class IkeAuthPskConfigUtils {
343         private static final String PSK_KEY = "PSK_KEY";
344 
345         @NonNull
toPersistableBundle( @onNull IkeAuthPskConfig config, @NonNull PersistableBundle result)346         public static PersistableBundle toPersistableBundle(
347                 @NonNull IkeAuthPskConfig config, @NonNull PersistableBundle result) {
348             result.putPersistableBundle(
349                     PSK_KEY, PersistableBundleUtils.fromByteArray(config.getPsk()));
350             return result;
351         }
352 
setBuilderByReadingPersistableBundle( @onNull PersistableBundle localAuthBundle, @NonNull PersistableBundle remoteAuthBundle, @NonNull IkeSessionParams.Builder builder)353         public static void setBuilderByReadingPersistableBundle(
354                 @NonNull PersistableBundle localAuthBundle,
355                 @NonNull PersistableBundle remoteAuthBundle,
356                 @NonNull IkeSessionParams.Builder builder) {
357             Objects.requireNonNull(localAuthBundle, "localAuthBundle was null");
358             Objects.requireNonNull(remoteAuthBundle, "remoteAuthBundle was null");
359 
360             final PersistableBundle localPskBundle = localAuthBundle.getPersistableBundle(PSK_KEY);
361             final PersistableBundle remotePskBundle =
362                     remoteAuthBundle.getPersistableBundle(PSK_KEY);
363             Objects.requireNonNull(localAuthBundle, "Local PSK was null");
364             Objects.requireNonNull(remoteAuthBundle, "Remote PSK was null");
365 
366             final byte[] localPsk = PersistableBundleUtils.toByteArray(localPskBundle);
367             final byte[] remotePsk = PersistableBundleUtils.toByteArray(remotePskBundle);
368             if (!Arrays.equals(localPsk, remotePsk)) {
369                 throw new IllegalArgumentException("Local PSK and remote PSK are different");
370             }
371             builder.setAuthPsk(localPsk);
372         }
373     }
374 
375     private static class IkeAuthDigitalSignConfigUtils {
376         private static final String END_CERT_KEY = "END_CERT_KEY";
377         private static final String INTERMEDIATE_CERTS_KEY = "INTERMEDIATE_CERTS_KEY";
378         private static final String PRIVATE_KEY_KEY = "PRIVATE_KEY_KEY";
379         private static final String TRUST_CERT_KEY = "TRUST_CERT_KEY";
380 
381         @NonNull
toPersistableBundle( @onNull IkeAuthDigitalSignLocalConfig config, @NonNull PersistableBundle result)382         public static PersistableBundle toPersistableBundle(
383                 @NonNull IkeAuthDigitalSignLocalConfig config, @NonNull PersistableBundle result) {
384             try {
385                 result.putPersistableBundle(
386                         END_CERT_KEY,
387                         PersistableBundleUtils.fromByteArray(
388                                 config.getClientEndCertificate().getEncoded()));
389 
390                 final List<X509Certificate> certList = config.getIntermediateCertificates();
391                 final List<byte[]> encodedCertList = new ArrayList<>(certList.size());
392                 for (X509Certificate cert : certList) {
393                     encodedCertList.add(cert.getEncoded());
394                 }
395 
396                 final PersistableBundle certsBundle =
397                         PersistableBundleUtils.fromList(
398                                 encodedCertList, PersistableBundleUtils::fromByteArray);
399                 result.putPersistableBundle(INTERMEDIATE_CERTS_KEY, certsBundle);
400             } catch (CertificateEncodingException e) {
401                 throw new IllegalArgumentException("Fail to encode certificate");
402             }
403 
404             // TODO: b/170670506 Consider putting PrivateKey in Android KeyStore
405             result.putPersistableBundle(
406                     PRIVATE_KEY_KEY,
407                     PersistableBundleUtils.fromByteArray(config.getPrivateKey().getEncoded()));
408             return result;
409         }
410 
411         @NonNull
toPersistableBundle( @onNull IkeAuthDigitalSignRemoteConfig config, @NonNull PersistableBundle result)412         public static PersistableBundle toPersistableBundle(
413                 @NonNull IkeAuthDigitalSignRemoteConfig config, @NonNull PersistableBundle result) {
414             try {
415                 X509Certificate caCert = config.getRemoteCaCert();
416                 if (caCert != null) {
417                     result.putPersistableBundle(
418                             TRUST_CERT_KEY,
419                             PersistableBundleUtils.fromByteArray(caCert.getEncoded()));
420                 }
421             } catch (CertificateEncodingException e) {
422                 throw new IllegalArgumentException("Fail to encode the certificate");
423             }
424 
425             return result;
426         }
427 
setBuilderByReadingPersistableBundle( @onNull PersistableBundle localAuthBundle, @NonNull PersistableBundle remoteAuthBundle, @NonNull IkeSessionParams.Builder builder)428         public static void setBuilderByReadingPersistableBundle(
429                 @NonNull PersistableBundle localAuthBundle,
430                 @NonNull PersistableBundle remoteAuthBundle,
431                 @NonNull IkeSessionParams.Builder builder) {
432             Objects.requireNonNull(localAuthBundle, "localAuthBundle was null");
433             Objects.requireNonNull(remoteAuthBundle, "remoteAuthBundle was null");
434 
435             // Deserialize localAuth
436             final PersistableBundle endCertBundle =
437                     localAuthBundle.getPersistableBundle(END_CERT_KEY);
438             Objects.requireNonNull(endCertBundle, "End cert was null");
439             final byte[] encodedCert = PersistableBundleUtils.toByteArray(endCertBundle);
440             final X509Certificate endCert = CertUtils.certificateFromByteArray(encodedCert);
441 
442             final PersistableBundle certsBundle =
443                     localAuthBundle.getPersistableBundle(INTERMEDIATE_CERTS_KEY);
444             Objects.requireNonNull(certsBundle, "Intermediate certs was null");
445             final List<byte[]> encodedCertList =
446                     PersistableBundleUtils.toList(certsBundle, PersistableBundleUtils::toByteArray);
447             final List<X509Certificate> certList = new ArrayList<>(encodedCertList.size());
448             for (byte[] encoded : encodedCertList) {
449                 certList.add(CertUtils.certificateFromByteArray(encoded));
450             }
451 
452             final PersistableBundle privateKeyBundle =
453                     localAuthBundle.getPersistableBundle(PRIVATE_KEY_KEY);
454             Objects.requireNonNull(privateKeyBundle, "PrivateKey bundle was null");
455             final PrivateKey privateKey =
456                     CertUtils.privateKeyFromByteArray(
457                             PersistableBundleUtils.toByteArray(privateKeyBundle));
458 
459             // Deserialize remoteAuth
460             final PersistableBundle trustCertBundle =
461                     remoteAuthBundle.getPersistableBundle(TRUST_CERT_KEY);
462 
463             X509Certificate caCert = null;
464             if (trustCertBundle != null) {
465                 final byte[] encodedCaCert = PersistableBundleUtils.toByteArray(trustCertBundle);
466                 caCert = CertUtils.certificateFromByteArray(encodedCaCert);
467             }
468 
469             builder.setAuthDigitalSignature(caCert, endCert, certList, privateKey);
470         }
471     }
472 
473     private static final class IkeAuthEapConfigUtils {
474         private static final String EAP_CONFIG_KEY = "EAP_CONFIG_KEY";
475 
476         @NonNull
toPersistableBundle( @onNull IkeAuthEapConfig config, @NonNull PersistableBundle result)477         public static PersistableBundle toPersistableBundle(
478                 @NonNull IkeAuthEapConfig config, @NonNull PersistableBundle result) {
479             result.putPersistableBundle(
480                     EAP_CONFIG_KEY,
481                     EapSessionConfigUtils.toPersistableBundle(config.getEapConfig()));
482             return result;
483         }
484 
setBuilderByReadingPersistableBundle( @onNull PersistableBundle localAuthBundle, @NonNull PersistableBundle remoteAuthBundle, @NonNull IkeSessionParams.Builder builder)485         public static void setBuilderByReadingPersistableBundle(
486                 @NonNull PersistableBundle localAuthBundle,
487                 @NonNull PersistableBundle remoteAuthBundle,
488                 @NonNull IkeSessionParams.Builder builder) {
489             // Deserialize localAuth
490             final PersistableBundle eapBundle =
491                     localAuthBundle.getPersistableBundle(EAP_CONFIG_KEY);
492             Objects.requireNonNull(eapBundle, "EAP Config was null");
493             final EapSessionConfig eapConfig =
494                     EapSessionConfigUtils.fromPersistableBundle(eapBundle);
495 
496             // Deserialize remoteAuth
497             final PersistableBundle trustCertBundle =
498                     remoteAuthBundle.getPersistableBundle(
499                             IkeAuthDigitalSignConfigUtils.TRUST_CERT_KEY);
500 
501             X509Certificate serverCaCert = null;
502             if (trustCertBundle != null) {
503                 final byte[] encodedCaCert = PersistableBundleUtils.toByteArray(trustCertBundle);
504                 serverCaCert = CertUtils.certificateFromByteArray(encodedCaCert);
505             }
506             builder.setAuthEap(serverCaCert, eapConfig);
507         }
508     }
509 
510     private static final class ConfigRequest {
511         private static final int IPV4_P_CSCF_ADDRESS = 1;
512         private static final int IPV6_P_CSCF_ADDRESS = 2;
513 
514         private static final String TYPE_KEY = "type";
515         private static final String ADDRESS_KEY = "address";
516 
517         public final int type;
518 
519         // Null when it is an empty request
520         @Nullable public final InetAddress address;
521 
ConfigRequest(IkeConfigRequest config)522         ConfigRequest(IkeConfigRequest config) {
523             if (config instanceof ConfigRequestIpv4PcscfServer) {
524                 type = IPV4_P_CSCF_ADDRESS;
525                 address = ((ConfigRequestIpv4PcscfServer) config).getAddress();
526             } else if (config instanceof ConfigRequestIpv6PcscfServer) {
527                 type = IPV6_P_CSCF_ADDRESS;
528                 address = ((ConfigRequestIpv6PcscfServer) config).getAddress();
529             } else {
530                 throw new IllegalStateException("Unknown TunnelModeChildConfigRequest");
531             }
532         }
533 
ConfigRequest(PersistableBundle in)534         ConfigRequest(PersistableBundle in) {
535             Objects.requireNonNull(in, "PersistableBundle was null");
536 
537             type = in.getInt(TYPE_KEY);
538 
539             String addressStr = in.getString(ADDRESS_KEY);
540             if (addressStr == null) {
541                 address = null;
542             } else {
543                 address = InetAddresses.parseNumericAddress(addressStr);
544             }
545         }
546 
547         @NonNull
toPersistableBundle()548         public PersistableBundle toPersistableBundle() {
549             final PersistableBundle result = new PersistableBundle();
550 
551             result.putInt(TYPE_KEY, type);
552             if (address != null) {
553                 result.putString(ADDRESS_KEY, address.getHostAddress());
554             }
555 
556             return result;
557         }
558     }
559 }
560