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