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