/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.wifi.aware; import static android.net.wifi.aware.Characteristics.WIFI_AWARE_CIPHER_SUITE_NCS_PK_128; import static android.net.wifi.aware.Characteristics.WIFI_AWARE_CIPHER_SUITE_NCS_PK_256; import static android.net.wifi.aware.Characteristics.WIFI_AWARE_CIPHER_SUITE_NCS_SK_128; import static android.net.wifi.aware.Characteristics.WIFI_AWARE_CIPHER_SUITE_NCS_SK_256; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import androidx.annotation.NonNull; import java.util.Arrays; import java.util.Objects; /** * Wi-Fi Aware data-path security config. The config is used with * {@link WifiAwareNetworkSpecifier.Builder#setDataPathSecurityConfig(WifiAwareDataPathSecurityConfig)} * to request a secure data-path. */ public final class WifiAwareDataPathSecurityConfig implements Parcelable { private final byte[] mPmk; private final String mPassphrase; private final byte[] mPmkId; private final int mCipherSuite; /** * Generate a security config with necessary parameters. Use {@link #isValid()} to check before * calling * {@link WifiAwareNetworkSpecifier.Builder#setDataPathSecurityConfig(WifiAwareDataPathSecurityConfig)} * @param passphrase The passphrase to be used to encrypt the link. * See {@link Builder#setPskPassphrase(String)} * @param cipherSuite The cipher suite to be used to encrypt the link. * See {@link Builder#setCipherSuite(int)} * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for * encrypting the data-path. See {@link Builder#setPmk(byte[])} * @param pmkId A PMKID (pairwise master key associated identifier, see IEEE 802.11) is * generated by Diffie-Hellman key exchange together with a Pairwise Master Key * (PMK), specifying the identifier associated to the key to use for encrypting * the data-path. See {@link Builder#setPmkId(byte[])} * @hide */ public WifiAwareDataPathSecurityConfig( @Characteristics.WifiAwareDataPathCipherSuites int cipherSuite, @Nullable byte[] pmk, @Nullable byte[] pmkId, @Nullable String passphrase) { mCipherSuite = cipherSuite; mPassphrase = passphrase; mPmk = pmk; mPmkId = pmkId; } private WifiAwareDataPathSecurityConfig(Parcel in) { mPmk = in.createByteArray(); mPassphrase = in.readString(); mPmkId = in.createByteArray(); mCipherSuite = in.readInt(); } public static final @NonNull Creator CREATOR = new Creator() { @Override public WifiAwareDataPathSecurityConfig createFromParcel(Parcel in) { return new WifiAwareDataPathSecurityConfig(in); } @Override public WifiAwareDataPathSecurityConfig[] newArray(int size) { return new WifiAwareDataPathSecurityConfig[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeByteArray(mPmk); dest.writeString(mPassphrase); dest.writeByteArray(mPmkId); dest.writeInt(mCipherSuite); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof WifiAwareDataPathSecurityConfig)) { return false; } WifiAwareDataPathSecurityConfig lhs = (WifiAwareDataPathSecurityConfig) obj; return mCipherSuite == lhs.mCipherSuite && Arrays.equals(mPmk, lhs.mPmk) && Objects.equals(mPassphrase, lhs.mPassphrase) && Arrays.equals(mPmkId, lhs.mPmkId); } @Override public int hashCode() { return Objects.hash(Arrays.hashCode(mPmk), mPassphrase, Arrays.hashCode(mPmkId), mCipherSuite); } @Override public String toString() { StringBuilder sb = new StringBuilder("WifiAwareDataPathSecurityConfig ["); sb.append("cipherSuite=").append(mCipherSuite) .append(", passphrase=") .append((TextUtils.isEmpty(mPassphrase)) ? "" : "") .append(", PMK=") .append((mPmk == null) ? "" : "") .append(", PMKID=") .append((mPmkId == null) ? "" : "") .append("]"); return sb.toString(); } /** * Check if the security config is valid. * @see Builder#Builder(int) * @return True if it is valid, false otherwise. * @hide */ public boolean isValid() { if (mCipherSuite == WIFI_AWARE_CIPHER_SUITE_NCS_SK_128 || mCipherSuite == WIFI_AWARE_CIPHER_SUITE_NCS_SK_256) { if (TextUtils.isEmpty(mPassphrase) && mPmk == null) { return false; } if (!TextUtils.isEmpty(mPassphrase) && mPmk != null) { return false; } if (mPmkId != null) { return false; } if (WifiAwareUtils.validatePassphrase(mPassphrase) && mPmk == null) { return true; } return TextUtils.isEmpty(mPassphrase) && WifiAwareUtils.validatePmk(mPmk); } else if (mCipherSuite == WIFI_AWARE_CIPHER_SUITE_NCS_PK_128 || mCipherSuite == WIFI_AWARE_CIPHER_SUITE_NCS_PK_256) { if (!WifiAwareUtils.validatePmk(mPmk) || !WifiAwareUtils.validatePmkId(mPmkId)) { return false; } return TextUtils.isEmpty(mPassphrase); } return false; } /** * Get the cipher suite specified in this config * @return one of {@code Characteristics#WIFI_AWARE_CIPHER_SUITE_*"} */ @Characteristics.WifiAwareDataPathCipherSuites public int getCipherSuite() { return mCipherSuite; } /** * Get the specified PMK in this config. * @see Builder#setPmk(byte[]) * @return A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for * encrypting the data-path. */ public @Nullable byte[] getPmk() { return mPmk; } /** * Get the specified PMKID in this config. * @return A PMKID (pairwise master key associated identifier, see IEEE 802.11) is generated * by Diffie-Hellman key exchange together with a Pairwise Master Key. */ public @Nullable byte[] getPmkId() { return mPmkId; } /** * Get the specified passphrase in this config. * @return The passphrase to be used to encrypt the link. */ public @Nullable String getPskPassphrase() { return mPassphrase; } /** * A builder class for a Wi-Fi Aware data-path security config to encrypt an Aware connection. */ public static final class Builder { private byte[] mPmk; private String mPassphrase; private byte[] mPmkId; private int mCipherSuite; /** * Create a builder for a Wi-Fi Aware data-path security config to encrypt the link with * specified cipher suite. Use {@link Characteristics#getSupportedCipherSuites()} to get the * supported capabilities of the device. *
    *
  • For shared key cipher suite * {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_SK_128} and * {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_SK_256}, either passphrase or PMK must * be set.
  • *
  • For public key cipher suite * {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_PK_128} and * {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_PK_256}. Both PMK and PMKID must be * set.
  • *
* @see WifiAwareNetworkSpecifier.Builder#setDataPathSecurityConfig(WifiAwareDataPathSecurityConfig) * @param cipherSuite The cipher suite to be used to encrypt the link. One of the * {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_SK_128}, * {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_SK_256}, * {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_PK_128} and * {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_PK_256}. */ public Builder(@Characteristics.WifiAwareDataPathCipherSuites int cipherSuite) { if (cipherSuite != WIFI_AWARE_CIPHER_SUITE_NCS_SK_128 && cipherSuite != WIFI_AWARE_CIPHER_SUITE_NCS_SK_256 && cipherSuite != WIFI_AWARE_CIPHER_SUITE_NCS_PK_128 && cipherSuite != WIFI_AWARE_CIPHER_SUITE_NCS_PK_256) { throw new IllegalArgumentException("Invalid cipher suite"); } mCipherSuite = cipherSuite; } /** * Configure the PSK Passphrase for the Wi-Fi Aware connection being requested. For shared * key cipher suite {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_SK_128} and * {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_SK_256}, either passphrase or PMK must * be set. * * @param pskPassphrase The passphrase to be used to encrypt the link. Alternatively, use * the {@link #setPmk(byte[])} to specify a PMK for shared key cipher * suite. * @return the current {@link Builder} builder, enabling chaining of builder methods. */ public @NonNull Builder setPskPassphrase(@NonNull String pskPassphrase) { if (!WifiAwareUtils.validatePassphrase(pskPassphrase)) { throw new IllegalArgumentException("Passphrase must meet length requirements"); } mPassphrase = pskPassphrase; return this; } /** * Configure the PMK for the Wi-Fi Aware connection being requested. For shared key cipher * suite {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_SK_128} and * {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_SK_256}, either passphrase or PMK must * be set. * For public key cipher suite {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_PK_128} * and {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_PK_256}. Both PMK and PMKID must * be set. * * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for * encrypting the data-path. Alternatively, use the * {@link #setPskPassphrase(String)} to specify a Passphrase instead for shared * key cipher suite. Use the {@link #setPmkId(byte[])} together for public key * cipher suite. * @return the current {@link Builder} builder, enabling chaining of builder * methods. */ public @NonNull Builder setPmk(@NonNull byte[] pmk) { if (!WifiAwareUtils.validatePmk(pmk)) { throw new IllegalArgumentException("PMK must 32 bytes"); } mPmk = pmk; return this; } /** * Configure the PMKID for the Wi-Fi Aware connection being requested. For public key cipher * suite {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_PK_128} and * {@link Characteristics#WIFI_AWARE_CIPHER_SUITE_NCS_PK_256}. both PMK and PMKID must set * {@link #setPmk(byte[])} * * @param pmkId A PMKID (pairwise master key associated identifier, see IEEE 802.11) is * generated by Diffie-Hellman key exchange together with a Pairwise Master Key * (PMK), specifying the identifier associated to the key to use for encrypting * the data-path. Use the {@link #setPmk(byte[])} together for public key * cipher suite. * @return the current {@link Builder} builder, enabling chaining of builder * methods. */ public @NonNull Builder setPmkId(@NonNull byte[] pmkId) { if (!WifiAwareUtils.validatePmkId(pmkId)) { throw new IllegalArgumentException("PMKID must 16 bytes"); } mPmkId = pmkId; return this; } /** * Create a {@link WifiAwareDataPathSecurityConfig} to set in * {@link WifiAwareNetworkSpecifier.Builder#setDataPathSecurityConfig(WifiAwareDataPathSecurityConfig)} to encrypt the link. * @return A {@link WifiAwareDataPathSecurityConfig} to be used for encrypting the Wi-Fi * Aware data-path. */ public @NonNull WifiAwareDataPathSecurityConfig build() { if (mPassphrase != null && mPmk != null) { throw new IllegalStateException( "Can only specify a Passphrase or a PMK - not both!"); } if (mCipherSuite == WIFI_AWARE_CIPHER_SUITE_NCS_SK_128 || mCipherSuite == WIFI_AWARE_CIPHER_SUITE_NCS_SK_256) { if (TextUtils.isEmpty(mPassphrase) && mPmk == null) { throw new IllegalStateException("Must set either PMK or Passphrase for " + "shared key cipher suite"); } if (mPmkId != null) { throw new IllegalStateException("PMKID should not set for " + "shared key cipher suite"); } } else { if (mPmk == null || mPmkId == null) { throw new IllegalStateException("Must set both PMK and PMKID for " + "public key cipher suite"); } if (!TextUtils.isEmpty(mPassphrase)) { throw new IllegalStateException("Passphrase is not support for public " + "key cipher suite"); } } return new WifiAwareDataPathSecurityConfig(mCipherSuite, mPmk, mPmkId, mPassphrase); } } }