1 /*
2  * Copyright (C) 2022 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.wifi.aware;
18 
19 import static android.net.wifi.aware.Characteristics.WIFI_AWARE_CIPHER_SUITE_NCS_PK_128;
20 import static android.net.wifi.aware.Characteristics.WIFI_AWARE_CIPHER_SUITE_NCS_PK_256;
21 import static android.net.wifi.aware.Characteristics.WIFI_AWARE_CIPHER_SUITE_NCS_SK_128;
22 import static android.net.wifi.aware.Characteristics.WIFI_AWARE_CIPHER_SUITE_NCS_SK_256;
23 
24 import android.annotation.Nullable;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 import android.text.TextUtils;
28 
29 import androidx.annotation.NonNull;
30 
31 import java.util.Arrays;
32 import java.util.Objects;
33 
34 /**
35  * Wi-Fi Aware data-path security config. The config is used with
36  * {@link WifiAwareNetworkSpecifier.Builder#setDataPathSecurityConfig(WifiAwareDataPathSecurityConfig)}
37  * to request a secure data-path.
38  */
39 public final class WifiAwareDataPathSecurityConfig implements Parcelable {
40     private final byte[] mPmk;
41     private final String mPassphrase;
42     private final byte[] mPmkId;
43     private final int mCipherSuite;
44 
45     /**
46      * Generate a security config with necessary parameters. Use {@link #isValid()} to check before
47      * calling
48      * {@link WifiAwareNetworkSpecifier.Builder#setDataPathSecurityConfig(WifiAwareDataPathSecurityConfig)}
49      * @param passphrase The passphrase to be used to encrypt the link.
50      *                      See {@link Builder#setPskPassphrase(String)}
51      * @param cipherSuite The cipher suite to be used to encrypt the link.
52      *                    See {@link Builder#setCipherSuite(int)}
53      * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for
54      *            encrypting the data-path. See {@link Builder#setPmk(byte[])}
55      * @param pmkId A PMKID (pairwise master key associated identifier, see IEEE 802.11) is
56      *              generated by Diffie-Hellman key exchange together with a Pairwise Master Key
57      *              (PMK), specifying the identifier associated to the key to use for encrypting
58      *              the data-path. See {@link Builder#setPmkId(byte[])}
59      * @hide
60      */
WifiAwareDataPathSecurityConfig( @haracteristics.WifiAwareDataPathCipherSuites int cipherSuite, @Nullable byte[] pmk, @Nullable byte[] pmkId, @Nullable String passphrase)61     public WifiAwareDataPathSecurityConfig(
62             @Characteristics.WifiAwareDataPathCipherSuites int cipherSuite,
63             @Nullable byte[] pmk, @Nullable byte[] pmkId, @Nullable String passphrase) {
64         mCipherSuite = cipherSuite;
65         mPassphrase = passphrase;
66         mPmk = pmk;
67         mPmkId = pmkId;
68     }
69 
WifiAwareDataPathSecurityConfig(Parcel in)70     private WifiAwareDataPathSecurityConfig(Parcel in) {
71         mPmk = in.createByteArray();
72         mPassphrase = in.readString();
73         mPmkId = in.createByteArray();
74         mCipherSuite = in.readInt();
75     }
76 
77     public static final @NonNull Creator<WifiAwareDataPathSecurityConfig> CREATOR =
78             new Creator<WifiAwareDataPathSecurityConfig>() {
79                 @Override
80                 public WifiAwareDataPathSecurityConfig createFromParcel(Parcel in) {
81                     return new WifiAwareDataPathSecurityConfig(in);
82                 }
83 
84                 @Override
85                 public WifiAwareDataPathSecurityConfig[] newArray(int size) {
86                     return new WifiAwareDataPathSecurityConfig[size];
87                 }
88             };
89 
90     @Override
describeContents()91     public int describeContents() {
92         return 0;
93     }
94 
95     @Override
writeToParcel(@onNull Parcel dest, int flags)96     public void writeToParcel(@NonNull Parcel dest, int flags) {
97         dest.writeByteArray(mPmk);
98         dest.writeString(mPassphrase);
99         dest.writeByteArray(mPmkId);
100         dest.writeInt(mCipherSuite);
101     }
102 
103     @Override
equals(Object obj)104     public boolean equals(Object obj) {
105         if (this == obj) {
106             return true;
107         }
108         if (!(obj instanceof WifiAwareDataPathSecurityConfig)) {
109             return false;
110         }
111 
112         WifiAwareDataPathSecurityConfig lhs = (WifiAwareDataPathSecurityConfig) obj;
113         return mCipherSuite == lhs.mCipherSuite
114                 && Arrays.equals(mPmk, lhs.mPmk)
115                 && Objects.equals(mPassphrase, lhs.mPassphrase)
116                 && Arrays.equals(mPmkId, lhs.mPmkId);
117     }
118 
119     @Override
hashCode()120     public int hashCode() {
121         return Objects.hash(Arrays.hashCode(mPmk), mPassphrase, Arrays.hashCode(mPmkId),
122                 mCipherSuite);
123     }
124 
125     @Override
toString()126     public String toString() {
127         StringBuilder sb = new StringBuilder("WifiAwareDataPathSecurityConfig [");
128         sb.append("cipherSuite=").append(mCipherSuite)
129                 .append(", passphrase=")
130                 .append((TextUtils.isEmpty(mPassphrase)) ? "<null>" : "<non-null>")
131                 .append(", PMK=")
132                 .append((mPmk == null) ? "<null>" : "<non-null>")
133                 .append(", PMKID=")
134                 .append((mPmkId == null) ? "<null>" : "<non-null>")
135                 .append("]");
136         return sb.toString();
137     }
138 
139     /**
140      * Check if the security config is valid.
141      * @see Builder#Builder(int)
142      * @return True if it is valid, false otherwise.
143      * @hide
144      */
isValid()145     public boolean isValid() {
146         if (mCipherSuite == WIFI_AWARE_CIPHER_SUITE_NCS_SK_128
147                 || mCipherSuite == WIFI_AWARE_CIPHER_SUITE_NCS_SK_256) {
148             if (TextUtils.isEmpty(mPassphrase) && mPmk == null) {
149                 return false;
150             }
151             if (!TextUtils.isEmpty(mPassphrase) && mPmk != null) {
152                 return false;
153             }
154             if (mPmkId != null) {
155                 return false;
156             }
157             if (WifiAwareUtils.validatePassphrase(mPassphrase) && mPmk == null) {
158                 return true;
159             }
160             return TextUtils.isEmpty(mPassphrase) && WifiAwareUtils.validatePmk(mPmk);
161         } else if (mCipherSuite == WIFI_AWARE_CIPHER_SUITE_NCS_PK_128
162                 || mCipherSuite == WIFI_AWARE_CIPHER_SUITE_NCS_PK_256) {
163             if (!WifiAwareUtils.validatePmk(mPmk) || !WifiAwareUtils.validatePmkId(mPmkId)) {
164                 return false;
165             }
166             return TextUtils.isEmpty(mPassphrase);
167         }
168         return false;
169     }
170 
171     /**
172      * Get the cipher suite specified in this config
173      * @return one of {@code Characteristics#WIFI_AWARE_CIPHER_SUITE_*"}
174      */
175     @Characteristics.WifiAwareDataPathCipherSuites
getCipherSuite()176     public int getCipherSuite() {
177         return mCipherSuite;
178     }
179 
180     /**
181      * Get the specified PMK in this config.
182      * @see Builder#setPmk(byte[])
183      * @return A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for
184      * encrypting the data-path.
185      */
getPmk()186     public @Nullable byte[] getPmk() {
187         return mPmk;
188     }
189 
190     /**
191      * Get the specified PMKID in this config.
192      * @return A PMKID (pairwise master key associated identifier, see IEEE 802.11) is generated
193      * by Diffie-Hellman key exchange together with a Pairwise Master Key.
194      */
getPmkId()195     public @Nullable byte[] getPmkId() {
196         return mPmkId;
197     }
198 
199     /**
200      * Get the specified passphrase in this config.
201      * @return The passphrase to be used to encrypt the link.
202      */
getPskPassphrase()203     public @Nullable String getPskPassphrase() {
204         return mPassphrase;
205     }
206 
207     /**
208      * A builder class for a Wi-Fi Aware data-path security config to encrypt an Aware connection.
209      */
210     public static final class Builder {
211         private  byte[] mPmk;
212         private  String mPassphrase;
213         private  byte[] mPmkId;
214         private  int mCipherSuite;
215 
216         /**
217          * Create a builder for a Wi-Fi Aware data-path security config to encrypt the link with
218          * specified cipher suite. Use {@link Characteristics#getSupportedCipherSuites()} to get the
219          * supported capabilities of the device.
220          * <ul>
221          * <li>For shared key cipher suite
222          * {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_SK_128} and
223          * {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_SK_256}, either passphrase or PMK must
224          * be set.</li>
225          * <li>For public key cipher suite
226          * {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_PK_128} and
227          * {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_PK_256}. Both PMK and PMKID must be
228          * set.</li>
229          * </ul>
230          * @see WifiAwareNetworkSpecifier.Builder#setDataPathSecurityConfig(WifiAwareDataPathSecurityConfig)
231          * @param cipherSuite The cipher suite to be used to encrypt the link. One of the
232          *                    {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_SK_128},
233          *                    {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_SK_256},
234          *                    {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_PK_128} and
235          *                    {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_PK_256}.
236          */
Builder(@haracteristics.WifiAwareDataPathCipherSuites int cipherSuite)237         public Builder(@Characteristics.WifiAwareDataPathCipherSuites int cipherSuite) {
238             if (cipherSuite != WIFI_AWARE_CIPHER_SUITE_NCS_SK_128
239                     && cipherSuite != WIFI_AWARE_CIPHER_SUITE_NCS_SK_256
240                     && cipherSuite != WIFI_AWARE_CIPHER_SUITE_NCS_PK_128
241                     && cipherSuite != WIFI_AWARE_CIPHER_SUITE_NCS_PK_256) {
242                 throw new IllegalArgumentException("Invalid cipher suite");
243             }
244             mCipherSuite = cipherSuite;
245         }
246 
247         /**
248          * Configure the PSK Passphrase for the Wi-Fi Aware connection being requested. For shared
249          * key cipher suite {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_SK_128} and
250          * {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_SK_256}, either passphrase or PMK must
251          * be set.
252          *
253          * @param pskPassphrase The passphrase to be used to encrypt the link. Alternatively, use
254          *                      the {@link #setPmk(byte[])} to specify a PMK for shared key cipher
255          *                      suite.
256          * @return the current {@link Builder} builder, enabling chaining of builder methods.
257          */
setPskPassphrase(@onNull String pskPassphrase)258         public @NonNull Builder setPskPassphrase(@NonNull String pskPassphrase) {
259             if (!WifiAwareUtils.validatePassphrase(pskPassphrase)) {
260                 throw new IllegalArgumentException("Passphrase must meet length requirements");
261             }
262             mPassphrase = pskPassphrase;
263             return this;
264         }
265 
266         /**
267          * Configure the PMK for the Wi-Fi Aware connection being requested. For shared key cipher
268          * suite {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_SK_128} and
269          * {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_SK_256}, either passphrase or PMK must
270          * be set.
271          * For public key cipher suite {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_PK_128}
272          * and {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_PK_256}. Both PMK and PMKID must
273          * be set.
274          *
275          * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for
276          *            encrypting the data-path. Alternatively, use the
277          *            {@link #setPskPassphrase(String)} to specify a Passphrase instead for shared
278          *            key cipher suite. Use the {@link #setPmkId(byte[])} together for public key
279          *            cipher suite.
280          * @return the current {@link Builder} builder, enabling chaining of builder
281          *         methods.
282          */
setPmk(@onNull byte[] pmk)283         public @NonNull Builder setPmk(@NonNull byte[] pmk) {
284             if (!WifiAwareUtils.validatePmk(pmk)) {
285                 throw new IllegalArgumentException("PMK must 32 bytes");
286             }
287             mPmk = pmk;
288             return this;
289         }
290 
291         /**
292          * Configure the PMKID for the Wi-Fi Aware connection being requested. For public key cipher
293          * suite {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_PK_128} and
294          * {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_PK_256}. both PMK and PMKID must set
295          * {@link #setPmk(byte[])}
296          *
297          * @param pmkId A PMKID (pairwise master key associated identifier, see IEEE 802.11) is
298          *              generated by Diffie-Hellman key exchange together with a Pairwise Master Key
299          *              (PMK), specifying the identifier associated to the key to use for encrypting
300          *              the data-path. Use the  {@link #setPmk(byte[])} together for public key
301          *              cipher suite.
302          * @return the current {@link Builder} builder, enabling chaining of builder
303          *         methods.
304          */
setPmkId(@onNull byte[] pmkId)305         public @NonNull Builder setPmkId(@NonNull byte[] pmkId) {
306             if (!WifiAwareUtils.validatePmkId(pmkId)) {
307                 throw new IllegalArgumentException("PMKID must 16 bytes");
308             }
309             mPmkId = pmkId;
310             return this;
311         }
312 
313         /**
314          * Create a {@link WifiAwareDataPathSecurityConfig} to set in
315          * {@link WifiAwareNetworkSpecifier.Builder#setDataPathSecurityConfig(WifiAwareDataPathSecurityConfig)} to encrypt the link.
316          * @return A {@link WifiAwareDataPathSecurityConfig} to be used for encrypting the Wi-Fi
317          * Aware data-path.
318          */
build()319         public @NonNull WifiAwareDataPathSecurityConfig build() {
320             if (mPassphrase != null && mPmk != null) {
321                 throw new IllegalStateException(
322                         "Can only specify a Passphrase or a PMK - not both!");
323             }
324             if (mCipherSuite == WIFI_AWARE_CIPHER_SUITE_NCS_SK_128
325                     || mCipherSuite == WIFI_AWARE_CIPHER_SUITE_NCS_SK_256) {
326                 if (TextUtils.isEmpty(mPassphrase) && mPmk == null) {
327                     throw new IllegalStateException("Must set either PMK or Passphrase for "
328                             + "shared key cipher suite");
329                 }
330                 if (mPmkId != null) {
331                     throw new IllegalStateException("PMKID should not set for "
332                             + "shared key cipher suite");
333                 }
334             } else {
335                 if (mPmk == null || mPmkId == null) {
336                     throw new IllegalStateException("Must set both PMK and PMKID for "
337                             + "public key cipher suite");
338                 }
339                 if (!TextUtils.isEmpty(mPassphrase)) {
340                     throw new IllegalStateException("Passphrase is not support for public "
341                             + "key cipher suite");
342                 }
343             }
344 
345             return new WifiAwareDataPathSecurityConfig(mCipherSuite, mPmk, mPmkId, mPassphrase);
346         }
347     }
348 }
349