1 /*
2  * Copyright (C) 2019 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;
18 
19 import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_PSK;
20 import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_RSA;
21 import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
22 
23 import static com.android.internal.annotations.VisibleForTesting.Visibility;
24 import static com.android.internal.util.Preconditions.checkStringNotEmpty;
25 
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.annotation.RequiresFeature;
29 import android.content.pm.PackageManager;
30 import android.os.Process;
31 import android.security.Credentials;
32 import android.security.KeyStore;
33 import android.security.keystore.AndroidKeyStoreProvider;
34 
35 import com.android.internal.annotations.VisibleForTesting;
36 import com.android.internal.net.VpnProfile;
37 
38 import java.io.IOException;
39 import java.nio.charset.StandardCharsets;
40 import java.security.GeneralSecurityException;
41 import java.security.KeyFactory;
42 import java.security.NoSuchAlgorithmException;
43 import java.security.PrivateKey;
44 import java.security.cert.CertificateEncodingException;
45 import java.security.cert.CertificateException;
46 import java.security.cert.X509Certificate;
47 import java.security.spec.InvalidKeySpecException;
48 import java.security.spec.PKCS8EncodedKeySpec;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.Base64;
52 import java.util.Collections;
53 import java.util.List;
54 import java.util.Objects;
55 
56 /**
57  * The Ikev2VpnProfile is a configuration for the platform setup of IKEv2/IPsec VPNs.
58  *
59  * <p>Together with VpnManager, this allows apps to provision IKEv2/IPsec VPNs that do not require
60  * the VPN app to constantly run in the background.
61  *
62  * @see VpnManager
63  * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296 - Internet Key
64  *     Exchange, Version 2 (IKEv2)</a>
65  */
66 public final class Ikev2VpnProfile extends PlatformVpnProfile {
67     /** Prefix for when a Private Key is an alias to look for in KeyStore @hide */
68     public static final String PREFIX_KEYSTORE_ALIAS = "KEYSTORE_ALIAS:";
69     /** Prefix for when a Private Key is stored directly in the profile @hide */
70     public static final String PREFIX_INLINE = "INLINE:";
71 
72     private static final String MISSING_PARAM_MSG_TMPL = "Required parameter was not provided: %s";
73     private static final String EMPTY_CERT = "";
74 
75     /** @hide */
76     public static final List<String> DEFAULT_ALGORITHMS =
77             Collections.unmodifiableList(Arrays.asList(
78                     IpSecAlgorithm.CRYPT_AES_CBC,
79                     IpSecAlgorithm.AUTH_HMAC_SHA256,
80                     IpSecAlgorithm.AUTH_HMAC_SHA384,
81                     IpSecAlgorithm.AUTH_HMAC_SHA512,
82                     IpSecAlgorithm.AUTH_CRYPT_AES_GCM));
83 
84     @NonNull private final String mServerAddr;
85     @NonNull private final String mUserIdentity;
86 
87     // PSK authentication
88     @Nullable private final byte[] mPresharedKey;
89 
90     // Username/Password, RSA authentication
91     @Nullable private final X509Certificate mServerRootCaCert;
92 
93     // Username/Password authentication
94     @Nullable private final String mUsername;
95     @Nullable private final String mPassword;
96 
97     // RSA Certificate authentication
98     @Nullable private final PrivateKey mRsaPrivateKey;
99     @Nullable private final X509Certificate mUserCert;
100 
101     @Nullable private final ProxyInfo mProxyInfo;
102     @NonNull private final List<String> mAllowedAlgorithms;
103     private final boolean mIsBypassable; // Defaults in builder
104     private final boolean mIsMetered; // Defaults in builder
105     private final int mMaxMtu; // Defaults in builder
106     private final boolean mIsRestrictedToTestNetworks;
107 
Ikev2VpnProfile( int type, @NonNull String serverAddr, @NonNull String userIdentity, @Nullable byte[] presharedKey, @Nullable X509Certificate serverRootCaCert, @Nullable String username, @Nullable String password, @Nullable PrivateKey rsaPrivateKey, @Nullable X509Certificate userCert, @Nullable ProxyInfo proxyInfo, @NonNull List<String> allowedAlgorithms, boolean isBypassable, boolean isMetered, int maxMtu, boolean restrictToTestNetworks)108     private Ikev2VpnProfile(
109             int type,
110             @NonNull String serverAddr,
111             @NonNull String userIdentity,
112             @Nullable byte[] presharedKey,
113             @Nullable X509Certificate serverRootCaCert,
114             @Nullable String username,
115             @Nullable String password,
116             @Nullable PrivateKey rsaPrivateKey,
117             @Nullable X509Certificate userCert,
118             @Nullable ProxyInfo proxyInfo,
119             @NonNull List<String> allowedAlgorithms,
120             boolean isBypassable,
121             boolean isMetered,
122             int maxMtu,
123             boolean restrictToTestNetworks) {
124         super(type);
125 
126         checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "Server address");
127         checkNotNull(userIdentity, MISSING_PARAM_MSG_TMPL, "User Identity");
128         checkNotNull(allowedAlgorithms, MISSING_PARAM_MSG_TMPL, "Allowed Algorithms");
129 
130         mServerAddr = serverAddr;
131         mUserIdentity = userIdentity;
132         mPresharedKey =
133                 presharedKey == null ? null : Arrays.copyOf(presharedKey, presharedKey.length);
134         mServerRootCaCert = serverRootCaCert;
135         mUsername = username;
136         mPassword = password;
137         mRsaPrivateKey = rsaPrivateKey;
138         mUserCert = userCert;
139         mProxyInfo = new ProxyInfo(proxyInfo);
140 
141         // UnmodifiableList doesn't make a defensive copy by default.
142         mAllowedAlgorithms = Collections.unmodifiableList(new ArrayList<>(allowedAlgorithms));
143 
144         mIsBypassable = isBypassable;
145         mIsMetered = isMetered;
146         mMaxMtu = maxMtu;
147         mIsRestrictedToTestNetworks = restrictToTestNetworks;
148 
149         validate();
150     }
151 
validate()152     private void validate() {
153         // Server Address not validated except to check an address was provided. This allows for
154         // dual-stack servers and hostname based addresses.
155         checkStringNotEmpty(mServerAddr, MISSING_PARAM_MSG_TMPL, "Server Address");
156         checkStringNotEmpty(mUserIdentity, MISSING_PARAM_MSG_TMPL, "User Identity");
157 
158         // IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6
159         // networks, the VPN must provide a link fulfilling the stricter of the two conditions
160         // (at least that of the IPv6 MTU).
161         if (mMaxMtu < LinkProperties.MIN_MTU_V6) {
162             throw new IllegalArgumentException(
163                     "Max MTU must be at least" + LinkProperties.MIN_MTU_V6);
164         }
165 
166         switch (mType) {
167             case TYPE_IKEV2_IPSEC_USER_PASS:
168                 checkNotNull(mUsername, MISSING_PARAM_MSG_TMPL, "Username");
169                 checkNotNull(mPassword, MISSING_PARAM_MSG_TMPL, "Password");
170 
171                 if (mServerRootCaCert != null) checkCert(mServerRootCaCert);
172 
173                 break;
174             case TYPE_IKEV2_IPSEC_PSK:
175                 checkNotNull(mPresharedKey, MISSING_PARAM_MSG_TMPL, "Preshared Key");
176                 break;
177             case TYPE_IKEV2_IPSEC_RSA:
178                 checkNotNull(mUserCert, MISSING_PARAM_MSG_TMPL, "User cert");
179                 checkNotNull(mRsaPrivateKey, MISSING_PARAM_MSG_TMPL, "RSA Private key");
180 
181                 checkCert(mUserCert);
182                 if (mServerRootCaCert != null) checkCert(mServerRootCaCert);
183 
184                 break;
185             default:
186                 throw new IllegalArgumentException("Invalid auth method set");
187         }
188 
189         validateAllowedAlgorithms(mAllowedAlgorithms);
190     }
191 
192     /**
193      * Validates that the allowed algorithms are a valid set for IPsec purposes
194      *
195      * <p>In order for the algorithm list to be a valid set, it must contain at least one algorithm
196      * that provides Authentication, and one that provides Encryption. Authenticated Encryption with
197      * Associated Data (AEAD) algorithms are counted as providing Authentication and Encryption.
198      *
199      * @param allowedAlgorithms The list to be validated
200      */
validateAllowedAlgorithms(@onNull List<String> algorithmNames)201     private static void validateAllowedAlgorithms(@NonNull List<String> algorithmNames) {
202         VpnProfile.validateAllowedAlgorithms(algorithmNames);
203 
204         // First, make sure no insecure algorithms were proposed.
205         if (algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_MD5)
206                 || algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_SHA1)) {
207             throw new IllegalArgumentException("Algorithm not supported for IKEv2 VPN profiles");
208         }
209 
210         // Validate that some valid combination (AEAD or AUTH + CRYPT) is present
211         if (hasAeadAlgorithms(algorithmNames) || hasNormalModeAlgorithms(algorithmNames)) {
212             return;
213         }
214 
215         throw new IllegalArgumentException("Algorithm set missing support for Auth, Crypt or both");
216     }
217 
218     /**
219      * Checks if the provided list has AEAD algorithms
220      *
221      * @hide
222      */
hasAeadAlgorithms(@onNull List<String> algorithmNames)223     public static boolean hasAeadAlgorithms(@NonNull List<String> algorithmNames) {
224         return algorithmNames.contains(IpSecAlgorithm.AUTH_CRYPT_AES_GCM);
225     }
226 
227     /**
228      * Checks the provided list has acceptable (non-AEAD) authentication and encryption algorithms
229      *
230      * @hide
231      */
hasNormalModeAlgorithms(@onNull List<String> algorithmNames)232     public static boolean hasNormalModeAlgorithms(@NonNull List<String> algorithmNames) {
233         final boolean hasCrypt = algorithmNames.contains(IpSecAlgorithm.CRYPT_AES_CBC);
234         final boolean hasAuth = algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_SHA256)
235                 || algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_SHA384)
236                 || algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_SHA512);
237 
238         return hasCrypt && hasAuth;
239     }
240 
241     /** Retrieves the server address string. */
242     @NonNull
getServerAddr()243     public String getServerAddr() {
244         return mServerAddr;
245     }
246 
247     /** Retrieves the user identity. */
248     @NonNull
getUserIdentity()249     public String getUserIdentity() {
250         return mUserIdentity;
251     }
252 
253     /**
254      * Retrieves the pre-shared key.
255      *
256      * <p>May be null if the profile is not using Pre-shared key authentication.
257      */
258     @Nullable
getPresharedKey()259     public byte[] getPresharedKey() {
260         return mPresharedKey == null ? null : Arrays.copyOf(mPresharedKey, mPresharedKey.length);
261     }
262 
263     /**
264      * Retrieves the certificate for the server's root CA.
265      *
266      * <p>May be null if the profile is not using RSA Digital Signature Authentication or
267      * Username/Password authentication
268      */
269     @Nullable
getServerRootCaCert()270     public X509Certificate getServerRootCaCert() {
271         return mServerRootCaCert;
272     }
273 
274     /**
275      * Retrieves the username.
276      *
277      * <p>May be null if the profile is not using Username/Password authentication
278      */
279     @Nullable
getUsername()280     public String getUsername() {
281         return mUsername;
282     }
283 
284     /**
285      * Retrieves the password.
286      *
287      * <p>May be null if the profile is not using Username/Password authentication
288      */
289     @Nullable
getPassword()290     public String getPassword() {
291         return mPassword;
292     }
293 
294     /**
295      * Retrieves the RSA private key.
296      *
297      * <p>May be null if the profile is not using RSA Digital Signature authentication
298      */
299     @Nullable
getRsaPrivateKey()300     public PrivateKey getRsaPrivateKey() {
301         return mRsaPrivateKey;
302     }
303 
304     /** Retrieves the user certificate, if any was set. */
305     @Nullable
getUserCert()306     public X509Certificate getUserCert() {
307         return mUserCert;
308     }
309 
310     /** Retrieves the proxy information if any was set */
311     @Nullable
getProxyInfo()312     public ProxyInfo getProxyInfo() {
313         return mProxyInfo;
314     }
315 
316     /** Returns all the algorithms allowed by this VPN profile. */
317     @NonNull
getAllowedAlgorithms()318     public List<String> getAllowedAlgorithms() {
319         return mAllowedAlgorithms;
320     }
321 
322     /** Returns whether or not the VPN profile should be bypassable. */
isBypassable()323     public boolean isBypassable() {
324         return mIsBypassable;
325     }
326 
327     /** Returns whether or not the VPN profile should be always considered metered. */
isMetered()328     public boolean isMetered() {
329         return mIsMetered;
330     }
331 
332     /** Retrieves the maximum MTU set for this VPN profile. */
getMaxMtu()333     public int getMaxMtu() {
334         return mMaxMtu;
335     }
336 
337     /**
338      * Returns whether or not this VPN profile is restricted to test networks.
339      *
340      * @hide
341      */
isRestrictedToTestNetworks()342     public boolean isRestrictedToTestNetworks() {
343         return mIsRestrictedToTestNetworks;
344     }
345 
346     @Override
hashCode()347     public int hashCode() {
348         return Objects.hash(
349                 mType,
350                 mServerAddr,
351                 mUserIdentity,
352                 Arrays.hashCode(mPresharedKey),
353                 mServerRootCaCert,
354                 mUsername,
355                 mPassword,
356                 mRsaPrivateKey,
357                 mUserCert,
358                 mProxyInfo,
359                 mAllowedAlgorithms,
360                 mIsBypassable,
361                 mIsMetered,
362                 mMaxMtu,
363                 mIsRestrictedToTestNetworks);
364     }
365 
366     @Override
equals(Object obj)367     public boolean equals(Object obj) {
368         if (!(obj instanceof Ikev2VpnProfile)) {
369             return false;
370         }
371 
372         final Ikev2VpnProfile other = (Ikev2VpnProfile) obj;
373         return mType == other.mType
374                 && Objects.equals(mServerAddr, other.mServerAddr)
375                 && Objects.equals(mUserIdentity, other.mUserIdentity)
376                 && Arrays.equals(mPresharedKey, other.mPresharedKey)
377                 && Objects.equals(mServerRootCaCert, other.mServerRootCaCert)
378                 && Objects.equals(mUsername, other.mUsername)
379                 && Objects.equals(mPassword, other.mPassword)
380                 && Objects.equals(mRsaPrivateKey, other.mRsaPrivateKey)
381                 && Objects.equals(mUserCert, other.mUserCert)
382                 && Objects.equals(mProxyInfo, other.mProxyInfo)
383                 && Objects.equals(mAllowedAlgorithms, other.mAllowedAlgorithms)
384                 && mIsBypassable == other.mIsBypassable
385                 && mIsMetered == other.mIsMetered
386                 && mMaxMtu == other.mMaxMtu
387                 && mIsRestrictedToTestNetworks == other.mIsRestrictedToTestNetworks;
388     }
389 
390     /**
391      * Builds a VpnProfile instance for internal use, based on the stored IKEv2/IPsec parameters.
392      *
393      * <p>Redundant authentication information (from previous calls to other setAuth* methods) will
394      * be discarded.
395      *
396      * @hide
397      */
398     @NonNull
toVpnProfile()399     public VpnProfile toVpnProfile() throws IOException, GeneralSecurityException {
400         final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */,
401                 mIsRestrictedToTestNetworks);
402         profile.type = mType;
403         profile.server = mServerAddr;
404         profile.ipsecIdentifier = mUserIdentity;
405         profile.proxy = mProxyInfo;
406         profile.setAllowedAlgorithms(mAllowedAlgorithms);
407         profile.isBypassable = mIsBypassable;
408         profile.isMetered = mIsMetered;
409         profile.maxMtu = mMaxMtu;
410         profile.areAuthParamsInline = true;
411         profile.saveLogin = true;
412 
413         switch (mType) {
414             case TYPE_IKEV2_IPSEC_USER_PASS:
415                 profile.username = mUsername;
416                 profile.password = mPassword;
417                 profile.ipsecCaCert =
418                         mServerRootCaCert == null ? "" : certificateToPemString(mServerRootCaCert);
419                 break;
420             case TYPE_IKEV2_IPSEC_PSK:
421                 profile.ipsecSecret = encodeForIpsecSecret(mPresharedKey);
422                 break;
423             case TYPE_IKEV2_IPSEC_RSA:
424                 profile.ipsecUserCert = certificateToPemString(mUserCert);
425                 profile.ipsecSecret =
426                         PREFIX_INLINE + encodeForIpsecSecret(mRsaPrivateKey.getEncoded());
427                 profile.ipsecCaCert =
428                         mServerRootCaCert == null ? "" : certificateToPemString(mServerRootCaCert);
429                 break;
430             default:
431                 throw new IllegalArgumentException("Invalid auth method set");
432         }
433 
434         return profile;
435     }
436 
437     /**
438      * Constructs a Ikev2VpnProfile from an internal-use VpnProfile instance.
439      *
440      * <p>Redundant authentication information (not related to profile type) will be discarded.
441      *
442      * @hide
443      */
444     @NonNull
fromVpnProfile(@onNull VpnProfile profile)445     public static Ikev2VpnProfile fromVpnProfile(@NonNull VpnProfile profile)
446             throws IOException, GeneralSecurityException {
447         return fromVpnProfile(profile, null);
448     }
449 
450     /**
451      * Builds the Ikev2VpnProfile from the given profile.
452      *
453      * @param profile the source VpnProfile to build from
454      * @param keyStore the Android Keystore instance to use to retrieve the private key, or null if
455      *     the private key is PEM-encoded into the profile.
456      * @return The IKEv2/IPsec VPN profile
457      * @hide
458      */
459     @NonNull
fromVpnProfile( @onNull VpnProfile profile, @Nullable KeyStore keyStore)460     public static Ikev2VpnProfile fromVpnProfile(
461             @NonNull VpnProfile profile, @Nullable KeyStore keyStore)
462             throws IOException, GeneralSecurityException {
463         final Builder builder = new Builder(profile.server, profile.ipsecIdentifier);
464         builder.setProxy(profile.proxy);
465         builder.setAllowedAlgorithms(profile.getAllowedAlgorithms());
466         builder.setBypassable(profile.isBypassable);
467         builder.setMetered(profile.isMetered);
468         builder.setMaxMtu(profile.maxMtu);
469         if (profile.isRestrictedToTestNetworks) {
470             builder.restrictToTestNetworks();
471         }
472 
473         switch (profile.type) {
474             case TYPE_IKEV2_IPSEC_USER_PASS:
475                 builder.setAuthUsernamePassword(
476                         profile.username,
477                         profile.password,
478                         certificateFromPemString(profile.ipsecCaCert));
479                 break;
480             case TYPE_IKEV2_IPSEC_PSK:
481                 builder.setAuthPsk(decodeFromIpsecSecret(profile.ipsecSecret));
482                 break;
483             case TYPE_IKEV2_IPSEC_RSA:
484                 final PrivateKey key;
485                 if (profile.ipsecSecret.startsWith(PREFIX_KEYSTORE_ALIAS)) {
486                     Objects.requireNonNull(keyStore, "Missing Keystore for aliased PrivateKey");
487 
488                     final String alias =
489                             profile.ipsecSecret.substring(PREFIX_KEYSTORE_ALIAS.length());
490                     key = AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
491                             keyStore, alias, Process.myUid());
492                 } else if (profile.ipsecSecret.startsWith(PREFIX_INLINE)) {
493                     key = getPrivateKey(profile.ipsecSecret.substring(PREFIX_INLINE.length()));
494                 } else {
495                     throw new IllegalArgumentException("Invalid RSA private key prefix");
496                 }
497 
498                 final X509Certificate userCert = certificateFromPemString(profile.ipsecUserCert);
499                 final X509Certificate serverRootCa = certificateFromPemString(profile.ipsecCaCert);
500                 builder.setAuthDigitalSignature(userCert, key, serverRootCa);
501                 break;
502             default:
503                 throw new IllegalArgumentException("Invalid auth method set");
504         }
505 
506         return builder.build();
507     }
508 
509     /**
510      * Validates that the VpnProfile is acceptable for the purposes of an Ikev2VpnProfile.
511      *
512      * @hide
513      */
isValidVpnProfile(@onNull VpnProfile profile)514     public static boolean isValidVpnProfile(@NonNull VpnProfile profile) {
515         if (profile.server.isEmpty() || profile.ipsecIdentifier.isEmpty()) {
516             return false;
517         }
518 
519         switch (profile.type) {
520             case TYPE_IKEV2_IPSEC_USER_PASS:
521                 if (profile.username.isEmpty() || profile.password.isEmpty()) {
522                     return false;
523                 }
524                 break;
525             case TYPE_IKEV2_IPSEC_PSK:
526                 if (profile.ipsecSecret.isEmpty()) {
527                     return false;
528                 }
529                 break;
530             case TYPE_IKEV2_IPSEC_RSA:
531                 if (profile.ipsecSecret.isEmpty() || profile.ipsecUserCert.isEmpty()) {
532                     return false;
533                 }
534                 break;
535             default:
536                 return false;
537         }
538 
539         return true;
540     }
541 
542     /**
543      * Converts a X509 Certificate to a PEM-formatted string.
544      *
545      * <p>Must be public due to runtime-package restrictions.
546      *
547      * @hide
548      */
549     @NonNull
550     @VisibleForTesting(visibility = Visibility.PRIVATE)
certificateToPemString(@ullable X509Certificate cert)551     public static String certificateToPemString(@Nullable X509Certificate cert)
552             throws IOException, CertificateEncodingException {
553         if (cert == null) {
554             return EMPTY_CERT;
555         }
556 
557         // Credentials.convertToPem outputs ASCII bytes.
558         return new String(Credentials.convertToPem(cert), StandardCharsets.US_ASCII);
559     }
560 
561     /**
562      * Decodes the provided Certificate(s).
563      *
564      * <p>Will use the first one if the certStr encodes more than one certificate.
565      */
566     @Nullable
certificateFromPemString(@ullable String certStr)567     private static X509Certificate certificateFromPemString(@Nullable String certStr)
568             throws CertificateException {
569         if (certStr == null || EMPTY_CERT.equals(certStr)) {
570             return null;
571         }
572 
573         try {
574             final List<X509Certificate> certs =
575                     Credentials.convertFromPem(certStr.getBytes(StandardCharsets.US_ASCII));
576             return certs.isEmpty() ? null : certs.get(0);
577         } catch (IOException e) {
578             throw new CertificateException(e);
579         }
580     }
581 
582     /** @hide */
583     @NonNull
encodeForIpsecSecret(@onNull byte[] secret)584     public static String encodeForIpsecSecret(@NonNull byte[] secret) {
585         checkNotNull(secret, MISSING_PARAM_MSG_TMPL, "secret");
586 
587         return Base64.getEncoder().encodeToString(secret);
588     }
589 
590     @NonNull
decodeFromIpsecSecret(@onNull String encoded)591     private static byte[] decodeFromIpsecSecret(@NonNull String encoded) {
592         checkNotNull(encoded, MISSING_PARAM_MSG_TMPL, "encoded");
593 
594         return Base64.getDecoder().decode(encoded);
595     }
596 
597     @NonNull
getPrivateKey(@onNull String keyStr)598     private static PrivateKey getPrivateKey(@NonNull String keyStr)
599             throws InvalidKeySpecException, NoSuchAlgorithmException {
600         final PKCS8EncodedKeySpec privateKeySpec =
601                 new PKCS8EncodedKeySpec(decodeFromIpsecSecret(keyStr));
602         final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
603         return keyFactory.generatePrivate(privateKeySpec);
604     }
605 
checkCert(@onNull X509Certificate cert)606     private static void checkCert(@NonNull X509Certificate cert) {
607         try {
608             certificateToPemString(cert);
609         } catch (GeneralSecurityException | IOException e) {
610             throw new IllegalArgumentException("Certificate could not be encoded");
611         }
612     }
613 
checkNotNull( final T reference, final String messageTemplate, final Object... messageArgs)614     private static @NonNull <T> T checkNotNull(
615             final T reference, final String messageTemplate, final Object... messageArgs) {
616         return Objects.requireNonNull(reference, String.format(messageTemplate, messageArgs));
617     }
618 
619     /** A incremental builder for IKEv2 VPN profiles */
620     public static final class Builder {
621         private int mType = -1;
622         @NonNull private final String mServerAddr;
623         @NonNull private final String mUserIdentity;
624 
625         // PSK authentication
626         @Nullable private byte[] mPresharedKey;
627 
628         // Username/Password, RSA authentication
629         @Nullable private X509Certificate mServerRootCaCert;
630 
631         // Username/Password authentication
632         @Nullable private String mUsername;
633         @Nullable private String mPassword;
634 
635         // RSA Certificate authentication
636         @Nullable private PrivateKey mRsaPrivateKey;
637         @Nullable private X509Certificate mUserCert;
638 
639         @Nullable private ProxyInfo mProxyInfo;
640         @NonNull private List<String> mAllowedAlgorithms = DEFAULT_ALGORITHMS;
641         private boolean mIsBypassable = false;
642         private boolean mIsMetered = true;
643         private int mMaxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT;
644         private boolean mIsRestrictedToTestNetworks = false;
645 
646         /**
647          * Creates a new builder with the basic parameters of an IKEv2/IPsec VPN.
648          *
649          * @param serverAddr the server that the VPN should connect to
650          * @param identity the identity string to be used for IKEv2 authentication
651          */
652         @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
Builder(@onNull String serverAddr, @NonNull String identity)653         public Builder(@NonNull String serverAddr, @NonNull String identity) {
654             checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "serverAddr");
655             checkNotNull(identity, MISSING_PARAM_MSG_TMPL, "identity");
656 
657             mServerAddr = serverAddr;
658             mUserIdentity = identity;
659         }
660 
resetAuthParams()661         private void resetAuthParams() {
662             mPresharedKey = null;
663             mServerRootCaCert = null;
664             mUsername = null;
665             mPassword = null;
666             mRsaPrivateKey = null;
667             mUserCert = null;
668         }
669 
670         /**
671          * Set the IKEv2 authentication to use the provided username/password.
672          *
673          * <p>Setting this will configure IKEv2 authentication using EAP-MSCHAPv2. Only one
674          * authentication method may be set. This method will overwrite any previously set
675          * authentication method.
676          *
677          * @param user the username to be used for EAP-MSCHAPv2 authentication
678          * @param pass the password to be used for EAP-MSCHAPv2 authentication
679          * @param serverRootCa the root certificate to be used for verifying the identity of the
680          *     server
681          * @return this {@link Builder} object to facilitate chaining of method calls
682          * @throws IllegalArgumentException if any of the certificates were invalid or of an
683          *     unrecognized format
684          */
685         @NonNull
686         @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
setAuthUsernamePassword( @onNull String user, @NonNull String pass, @Nullable X509Certificate serverRootCa)687         public Builder setAuthUsernamePassword(
688                 @NonNull String user,
689                 @NonNull String pass,
690                 @Nullable X509Certificate serverRootCa) {
691             checkNotNull(user, MISSING_PARAM_MSG_TMPL, "user");
692             checkNotNull(pass, MISSING_PARAM_MSG_TMPL, "pass");
693 
694             // Test to make sure all auth params can be encoded safely.
695             if (serverRootCa != null) checkCert(serverRootCa);
696 
697             resetAuthParams();
698             mUsername = user;
699             mPassword = pass;
700             mServerRootCaCert = serverRootCa;
701             mType = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
702             return this;
703         }
704 
705         /**
706          * Set the IKEv2 authentication to use Digital Signature Authentication with the given key.
707          *
708          * <p>Setting this will configure IKEv2 authentication using a Digital Signature scheme.
709          * Only one authentication method may be set. This method will overwrite any previously set
710          * authentication method.
711          *
712          * @param userCert the username to be used for RSA Digital signiture authentication
713          * @param key the PrivateKey instance associated with the user ceritificate, used for
714          *     constructing the signature
715          * @param serverRootCa the root certificate to be used for verifying the identity of the
716          *     server
717          * @return this {@link Builder} object to facilitate chaining of method calls
718          * @throws IllegalArgumentException if any of the certificates were invalid or of an
719          *     unrecognized format
720          */
721         @NonNull
722         @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
setAuthDigitalSignature( @onNull X509Certificate userCert, @NonNull PrivateKey key, @Nullable X509Certificate serverRootCa)723         public Builder setAuthDigitalSignature(
724                 @NonNull X509Certificate userCert,
725                 @NonNull PrivateKey key,
726                 @Nullable X509Certificate serverRootCa) {
727             checkNotNull(userCert, MISSING_PARAM_MSG_TMPL, "userCert");
728             checkNotNull(key, MISSING_PARAM_MSG_TMPL, "key");
729 
730             // Test to make sure all auth params can be encoded safely.
731             checkCert(userCert);
732             if (serverRootCa != null) checkCert(serverRootCa);
733 
734             resetAuthParams();
735             mUserCert = userCert;
736             mRsaPrivateKey = key;
737             mServerRootCaCert = serverRootCa;
738             mType = VpnProfile.TYPE_IKEV2_IPSEC_RSA;
739             return this;
740         }
741 
742         /**
743          * Set the IKEv2 authentication to use Preshared keys.
744          *
745          * <p>Setting this will configure IKEv2 authentication using a Preshared Key. Only one
746          * authentication method may be set. This method will overwrite any previously set
747          * authentication method.
748          *
749          * @param psk the key to be used for Pre-Shared Key authentication
750          * @return this {@link Builder} object to facilitate chaining of method calls
751          */
752         @NonNull
753         @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
setAuthPsk(@onNull byte[] psk)754         public Builder setAuthPsk(@NonNull byte[] psk) {
755             checkNotNull(psk, MISSING_PARAM_MSG_TMPL, "psk");
756 
757             resetAuthParams();
758             mPresharedKey = psk;
759             mType = VpnProfile.TYPE_IKEV2_IPSEC_PSK;
760             return this;
761         }
762 
763         /**
764          * Sets whether apps can bypass this VPN connection.
765          *
766          * <p>By default, all traffic from apps are forwarded through the VPN interface and it is
767          * not possible for unprivileged apps to side-step the VPN. If a VPN is set to bypassable,
768          * apps may use methods such as {@link Network#getSocketFactory} or {@link
769          * Network#openConnection} to instead send/receive directly over the underlying network or
770          * any other network they have permissions for.
771          *
772          * @param isBypassable Whether or not the VPN should be considered bypassable. Defaults to
773          *     {@code false}.
774          * @return this {@link Builder} object to facilitate chaining of method calls
775          */
776         @NonNull
777         @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
setBypassable(boolean isBypassable)778         public Builder setBypassable(boolean isBypassable) {
779             mIsBypassable = isBypassable;
780             return this;
781         }
782 
783         /**
784          * Sets a proxy for the VPN network.
785          *
786          * <p>Note that this proxy is only a recommendation and it may be ignored by apps.
787          *
788          * @param proxy the ProxyInfo to be set for the VPN network
789          * @return this {@link Builder} object to facilitate chaining of method calls
790          */
791         @NonNull
792         @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
setProxy(@ullable ProxyInfo proxy)793         public Builder setProxy(@Nullable ProxyInfo proxy) {
794             mProxyInfo = proxy;
795             return this;
796         }
797 
798         /**
799          * Set the upper bound of the maximum transmission unit (MTU) of the VPN interface.
800          *
801          * <p>If it is not set, a safe value will be used. Additionally, the actual link MTU will be
802          * dynamically calculated/updated based on the underlying link's mtu.
803          *
804          * @param mtu the MTU (in bytes) of the VPN interface
805          * @return this {@link Builder} object to facilitate chaining of method calls
806          * @throws IllegalArgumentException if the value is not at least the minimum IPv6 MTU (1280)
807          */
808         @NonNull
809         @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
setMaxMtu(int mtu)810         public Builder setMaxMtu(int mtu) {
811             // IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6
812             // networks, the VPN must provide a link fulfilling the stricter of the two conditions
813             // (at least that of the IPv6 MTU).
814             if (mtu < LinkProperties.MIN_MTU_V6) {
815                 throw new IllegalArgumentException(
816                         "Max MTU must be at least " + LinkProperties.MIN_MTU_V6);
817             }
818             mMaxMtu = mtu;
819             return this;
820         }
821 
822         /**
823          * Marks the VPN network as metered.
824          *
825          * <p>A VPN network is classified as metered when the user is sensitive to heavy data usage
826          * due to monetary costs and/or data limitations. In such cases, you should set this to
827          * {@code true} so that apps on the system can avoid doing large data transfers. Otherwise,
828          * set this to {@code false}. Doing so would cause VPN network to inherit its meteredness
829          * from the underlying network.
830          *
831          * @param isMetered {@code true} if the VPN network should be treated as metered regardless
832          *     of underlying network meteredness. Defaults to {@code true}.
833          * @return this {@link Builder} object to facilitate chaining of method calls
834          * @see NetworkCapabilities#NET_CAPABILITY_NOT_METERED
835          */
836         @NonNull
837         @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
setMetered(boolean isMetered)838         public Builder setMetered(boolean isMetered) {
839             mIsMetered = isMetered;
840             return this;
841         }
842 
843         /**
844          * Sets the allowable set of IPsec algorithms
845          *
846          * <p>If set, this will constrain the set of algorithms that the IPsec tunnel will use for
847          * integrity verification and encryption to the provided list.
848          *
849          * <p>The set of allowed IPsec algorithms is defined in {@link IpSecAlgorithm}. Adding of
850          * algorithms that are considered insecure (such as AUTH_HMAC_MD5 and AUTH_HMAC_SHA1) is not
851          * permitted, and will result in an IllegalArgumentException being thrown.
852          *
853          * <p>The provided algorithm list must contain at least one algorithm that provides
854          * Authentication, and one that provides Encryption. Authenticated Encryption with
855          * Associated Data (AEAD) algorithms provide both Authentication and Encryption.
856          *
857          * <p>By default, this profile will use any algorithm defined in {@link IpSecAlgorithm},
858          * with the exception of those considered insecure (as described above).
859          *
860          * @param algorithmNames the list of supported IPsec algorithms
861          * @return this {@link Builder} object to facilitate chaining of method calls
862          * @see IpSecAlgorithm
863          */
864         @NonNull
865         @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
setAllowedAlgorithms(@onNull List<String> algorithmNames)866         public Builder setAllowedAlgorithms(@NonNull List<String> algorithmNames) {
867             checkNotNull(algorithmNames, MISSING_PARAM_MSG_TMPL, "algorithmNames");
868             validateAllowedAlgorithms(algorithmNames);
869 
870             mAllowedAlgorithms = algorithmNames;
871             return this;
872         }
873 
874         /**
875          * Restricts this profile to use test networks (only).
876          *
877          * <p>This method is for testing only, and must not be used by apps. Calling
878          * provisionVpnProfile() with a profile where test-network usage is enabled will require the
879          * MANAGE_TEST_NETWORKS permission.
880          *
881          * @hide
882          */
883         @NonNull
884         @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
restrictToTestNetworks()885         public Builder restrictToTestNetworks() {
886             mIsRestrictedToTestNetworks = true;
887             return this;
888         }
889 
890         /**
891          * Validates, builds and provisions the VpnProfile.
892          *
893          * @throws IllegalArgumentException if any of the required keys or values were invalid
894          */
895         @NonNull
896         @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
build()897         public Ikev2VpnProfile build() {
898             return new Ikev2VpnProfile(
899                     mType,
900                     mServerAddr,
901                     mUserIdentity,
902                     mPresharedKey,
903                     mServerRootCaCert,
904                     mUsername,
905                     mPassword,
906                     mRsaPrivateKey,
907                     mUserCert,
908                     mProxyInfo,
909                     mAllowedAlgorithms,
910                     mIsBypassable,
911                     mIsMetered,
912                     mMaxMtu,
913                     mIsRestrictedToTestNetworks);
914         }
915     }
916 }
917