diff options
6 files changed, 859 insertions, 11 deletions
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java index 18aae534d098..a62a0fb582f1 100644 --- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java +++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java @@ -61,6 +61,21 @@ public final class PasspointConfiguration implements Parcelable { credential.equals(that.credential)); } + /** + * Validate the configuration data. + * + * @return true on success or false on failure + */ + public boolean validate() { + if (homeSp == null || !homeSp.validate()) { + return false; + } + if (credential == null || !credential.validate()) { + return false; + } + return true; + } + public static final Creator<PasspointConfiguration> CREATOR = new Creator<PasspointConfiguration>() { @Override diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java index 92dbd8afb2d3..57e65eb7a190 100644 --- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java +++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java @@ -16,15 +16,21 @@ package android.net.wifi.hotspot2.pps; +import android.net.wifi.EAPConstants; import android.net.wifi.ParcelUtil; import android.os.Parcelable; import android.os.Parcel; import android.text.TextUtils; +import android.util.Log; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; /** * Class representing Credential subtree in the PerProviderSubscription (PPS) @@ -40,6 +46,14 @@ import java.util.Arrays; * @hide */ public final class Credential implements Parcelable { + private static final String TAG = "Credential"; + + /** + * Max string length for realm. Refer to Credential/Realm node in Hotspot 2.0 Release 2 + * Technical Specification Section 9.1 for more info. + */ + private static final int MAX_REALM_LENGTH = 253; + /** * The realm associated with this credential. It will be used to determine * if this credential can be used to authenticate with a given hotspot by @@ -53,6 +67,26 @@ public final class Credential implements Parcelable { */ public static final class UserCredential implements Parcelable { /** + * Maximum string length for username. Refer to Credential/UsernamePassword/Username + * node in Hotspot 2.0 Release 2 Technical Specification Section 9.1 for more info. + */ + private static final int MAX_USERNAME_LENGTH = 63; + + /** + * Maximum string length for password. Refer to Credential/UsernamePassword/Password + * in Hotspot 2.0 Release 2 Technical Specification Section 9.1 for more info. + */ + private static final int MAX_PASSWORD_LENGTH = 255; + + /** + * Supported Non-EAP inner methods. Refer to + * Credential/UsernamePassword/EAPMethod/InnerEAPType in Hotspot 2.0 Release 2 Technical + * Specification Section 9.1 for more info. + */ + private static final Set<String> SUPPORTED_AUTH = + new HashSet<String>(Arrays.asList("PAP", "CHAP", "MS-CHAP", "MS-CHAP-V2")); + + /** * Username of the credential. */ public String username = null; @@ -104,6 +138,44 @@ public final class Credential implements Parcelable { TextUtils.equals(nonEapInnerMethod, that.nonEapInnerMethod); } + /** + * Validate the configuration data. + * + * @return true on success or false on failure + */ + public boolean validate() { + if (TextUtils.isEmpty(username)) { + Log.d(TAG, "Missing username"); + return false; + } + if (username.length() > MAX_USERNAME_LENGTH) { + Log.d(TAG, "username exceeding maximum length: " + username.length()); + return false; + } + + if (TextUtils.isEmpty(password)) { + Log.d(TAG, "Missing password"); + return false; + } + if (password.length() > MAX_PASSWORD_LENGTH) { + Log.d(TAG, "password exceeding maximum length: " + password.length()); + return false; + } + + // Only supports EAP-TTLS for user credential. + if (eapType != EAPConstants.EAP_TTLS) { + Log.d(TAG, "Invalid EAP Type for user credential: " + eapType); + return false; + } + + // Verify Non-EAP inner method for EAP-TTLS. + if (!SUPPORTED_AUTH.contains(nonEapInnerMethod)) { + Log.d(TAG, "Invalid non-EAP inner method for EAP-TTLS: " + nonEapInnerMethod); + return false; + } + return true; + } + public static final Creator<UserCredential> CREATOR = new Creator<UserCredential>() { @Override @@ -125,12 +197,22 @@ public final class Credential implements Parcelable { public UserCredential userCredential = null; /** - * Certificate based credential. + * Certificate based credential. This is used for EAP-TLS. * Contains fields under PerProviderSubscription/Credential/DigitalCertificate subtree. */ public static final class CertificateCredential implements Parcelable { /** - * Certificate type. Valid values are "802.1ar" and "x509v3". + * Supported certificate types. + */ + private static final String CERT_TYPE_X509V3 = "x509v3"; + + /** + * Certificate SHA-256 fingerprint length. + */ + private static final int CERT_SHA256_FINGER_PRINT_LENGTH = 32; + + /** + * Certificate type. */ public String certType = null; @@ -164,6 +246,24 @@ public final class Credential implements Parcelable { Arrays.equals(certSha256FingerPrint, that.certSha256FingerPrint); } + /** + * Validate the configuration data. + * + * @return true on success or false on failure + */ + public boolean validate() { + if (!TextUtils.equals(CERT_TYPE_X509V3, certType)) { + Log.d(TAG, "Unsupported certificate type: " + certType); + return false; + } + if (certSha256FingerPrint == null || + certSha256FingerPrint.length != CERT_SHA256_FINGER_PRINT_LENGTH) { + Log.d(TAG, "Invalid SHA-256 fingerprint"); + return false; + } + return true; + } + public static final Creator<CertificateCredential> CREATOR = new Creator<CertificateCredential>() { @Override @@ -188,7 +288,14 @@ public final class Credential implements Parcelable { */ public static final class SimCredential implements Parcelable { /** - * International Mobile device Subscriber Identity. + * Maximum string length for IMSI. + */ + public static final int MAX_IMSI_LENGTH = 15; + + /** + * International Mobile Subscriber Identity, is used to identify the user + * of a cellular network and is a unique identification associated with all + * cellular networks */ public String imsi = null; @@ -225,6 +332,26 @@ public final class Credential implements Parcelable { dest.writeInt(eapType); } + /** + * Validate the configuration data. + * + * @return true on success or false on failure + */ + public boolean validate() { + // Note: this only validate the format of IMSI string itself. Additional verification + // will be done by WifiService at the time of provisioning to verify against the IMSI + // of the SIM card installed in the device. + if (!verifyImsi()) { + return false; + } + if (eapType != EAPConstants.EAP_SIM && eapType != EAPConstants.EAP_AKA && + eapType != EAPConstants.EAP_AKA_PRIME) { + Log.d(TAG, "Invalid EAP Type for SIM credential: " + eapType); + return false; + } + return true; + } + public static final Creator<SimCredential> CREATOR = new Creator<SimCredential>() { @Override @@ -240,6 +367,43 @@ public final class Credential implements Parcelable { return new SimCredential[size]; } }; + + /** + * Verify the IMSI (International Mobile Subscriber Identity) string. The string + * should contain zero or more numeric digits, and might ends with a "*" for prefix + * matching. + * + * @return true if IMSI is valid, false otherwise. + */ + private boolean verifyImsi() { + if (TextUtils.isEmpty(imsi)) { + Log.d(TAG, "Missing IMSI"); + return false; + } + if (imsi.length() > MAX_IMSI_LENGTH) { + Log.d(TAG, "IMSI exceeding maximum length: " + imsi.length()); + return false; + } + + // Locate the first non-digit character. + int nonDigit; + char stopChar = '\0'; + for (nonDigit = 0; nonDigit < imsi.length(); nonDigit++) { + stopChar = imsi.charAt(nonDigit); + if (stopChar < '0' || stopChar > '9') { + break; + } + } + + if (nonDigit == imsi.length()) { + return true; + } + else if (nonDigit == imsi.length()-1 && stopChar == '*') { + // Prefix matching. + return true; + } + return false; + } } public SimCredential simCredential = null; @@ -296,6 +460,42 @@ public final class Credential implements Parcelable { isPrivateKeyEquals(clientPrivateKey, that.clientPrivateKey); } + /** + * Validate the configuration data. + * + * @return true on success or false on failure + */ + public boolean validate() { + if (TextUtils.isEmpty(realm)) { + Log.d(TAG, "Missing realm"); + return false; + } + if (realm.length() > MAX_REALM_LENGTH) { + Log.d(TAG, "realm exceeding maximum length: " + realm.length()); + return false; + } + + // Verify the credential. + if (userCredential != null) { + if (!verifyUserCredential()) { + return false; + } + } else if (certCredential != null) { + if (!verifyCertCredential()) { + return false; + } + } else if (simCredential != null) { + if (!verifySimCredential()) { + return false; + } + } else { + Log.d(TAG, "Missing required credential"); + return false; + } + + return true; + } + public static final Creator<Credential> CREATOR = new Creator<Credential>() { @Override @@ -317,6 +517,91 @@ public final class Credential implements Parcelable { } }; + /** + * Verify user credential. + * + * @return true if user credential is valid, false otherwise. + */ + private boolean verifyUserCredential() { + if (userCredential == null) { + Log.d(TAG, "Missing user credential"); + return false; + } + if (certCredential != null || simCredential != null) { + Log.d(TAG, "Contained more than one type of credential"); + return false; + } + if (!userCredential.validate()) { + return false; + } + if (caCertificate == null) { + Log.d(TAG, "Missing CA Certificate for user credential"); + return false; + } + return true; + } + + /** + * Verify certificate credential, which is used for EAP-TLS. This will verify + * that the necessary client key and certificates are provided. + * + * @return true if certificate credential is valid, false otherwise. + */ + private boolean verifyCertCredential() { + if (certCredential == null) { + Log.d(TAG, "Missing certificate credential"); + return false; + } + if (userCredential != null || simCredential != null) { + Log.d(TAG, "Contained more than one type of credential"); + return false; + } + + if (!certCredential.validate()) { + return false; + } + + // Verify required key and certificates for certificate credential. + if (caCertificate == null) { + Log.d(TAG, "Missing CA Certificate for certificate credential"); + return false; + } + if (clientPrivateKey == null) { + Log.d(TAG, "Missing client private key for certificate credential"); + return false; + } + try { + // Verify SHA-256 fingerprint for client certificate. + if (!verifySha256Fingerprint(clientCertificateChain, + certCredential.certSha256FingerPrint)) { + Log.d(TAG, "SHA-256 fingerprint mismatch"); + return false; + } + } catch (NoSuchAlgorithmException | CertificateEncodingException e) { + Log.d(TAG, "Failed to verify SHA-256 fingerprint: " + e.getMessage()); + return false; + } + + return true; + } + + /** + * Verify SIM credential. + * + * @return true if SIM credential is valid, false otherwise. + */ + private boolean verifySimCredential() { + if (simCredential == null) { + Log.d(TAG, "Missing SIM credential"); + return false; + } + if (userCredential != null || certCredential != null) { + Log.d(TAG, "Contained more than one type of credential"); + return false; + } + return simCredential.validate(); + } + private static boolean isPrivateKeyEquals(PrivateKey key1, PrivateKey key2) { if (key1 == null && key2 == null) { return true; @@ -373,4 +658,31 @@ public final class Credential implements Parcelable { return true; } + + /** + * Verify that the digest for a certificate in the certificate chain matches expected + * fingerprint. The certificate that matches the fingerprint is the client certificate. + * + * @param certChain Chain of certificates + * @param expectedFingerprint The expected SHA-256 digest of the client certificate + * @return true if the certificate chain contains a matching certificate, false otherwise + * @throws NoSuchAlgorithmException + * @throws CertificateEncodingException + */ + private static boolean verifySha256Fingerprint(X509Certificate[] certChain, + byte[] expectedFingerprint) + throws NoSuchAlgorithmException, CertificateEncodingException { + if (certChain == null) { + return false; + } + MessageDigest digester = MessageDigest.getInstance("SHA-256"); + for (X509Certificate certificate : certChain) { + digester.reset(); + byte[] fingerprint = digester.digest(certificate.getEncoded()); + if (Arrays.equals(expectedFingerprint, fingerprint)) { + return true; + } + } + return false; + } } diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java index 2acc8bec8007..5837c06499e3 100644 --- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java +++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java @@ -19,6 +19,7 @@ package android.net.wifi.hotspot2.pps; import android.os.Parcelable; import android.os.Parcel; import android.text.TextUtils; +import android.util.Log; import java.util.Arrays; @@ -34,6 +35,8 @@ import java.util.Arrays; * @hide */ public final class HomeSP implements Parcelable { + private static final String TAG = "HomeSP"; + /** * FQDN (Fully Qualified Domain Name) of this home service provider. */ @@ -77,6 +80,23 @@ public final class HomeSP implements Parcelable { Arrays.equals(roamingConsortiumOIs, that.roamingConsortiumOIs); } + /** + * Validate HomeSP data. + * + * @return true on success or false on failure + */ + public boolean validate() { + if (TextUtils.isEmpty(fqdn)) { + Log.d(TAG, "Missing FQDN"); + return false; + } + if (TextUtils.isEmpty(friendlyName)) { + Log.d(TAG, "Missing friendly name"); + return false; + } + return true; + } + public static final Creator<HomeSP> CREATOR = new Creator<HomeSP>() { @Override diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java index be11f0ee64c0..b4a3acf08975 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java @@ -16,8 +16,10 @@ package android.net.wifi.hotspot2; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import android.net.wifi.EAPConstants; import android.net.wifi.hotspot2.pps.Credential; import android.net.wifi.hotspot2.pps.HomeSP; import android.os.Parcel; @@ -44,7 +46,9 @@ public class PasspointConfigurationTest { cred.realm = "realm"; cred.userCredential = null; cred.certCredential = null; - cred.simCredential = null; + cred.simCredential = new Credential.SimCredential(); + cred.simCredential.imsi = "1234*"; + cred.simCredential.eapType = EAPConstants.EAP_SIM; cred.caCertificate = null; cred.clientCertificateChain = null; cred.clientPrivateKey = null; @@ -61,11 +65,20 @@ public class PasspointConfigurationTest { assertTrue(readConfig.equals(writeConfig)); } + /** + * Verify parcel read/write for a default configuration. + * + * @throws Exception + */ @Test public void verifyParcelWithDefault() throws Exception { verifyParcel(new PasspointConfiguration()); } + /** + * Verify parcel read/write for a configuration that contained both HomeSP and Credential. + * @throws Exception + */ @Test public void verifyParcelWithHomeSPAndCredential() throws Exception { PasspointConfiguration config = new PasspointConfiguration(); @@ -74,6 +87,11 @@ public class PasspointConfigurationTest { verifyParcel(config); } + /** + * Verify parcel read/write for a configuration that contained only HomeSP. + * + * @throws Exception + */ @Test public void verifyParcelWithHomeSPOnly() throws Exception { PasspointConfiguration config = new PasspointConfiguration(); @@ -81,10 +99,63 @@ public class PasspointConfigurationTest { verifyParcel(config); } + /** + * Verify parcel read/write for a configuration that contained only Credential. + * + * @throws Exception + */ @Test public void verifyParcelWithCredentialOnly() throws Exception { PasspointConfiguration config = new PasspointConfiguration(); config.credential = createCredential(); verifyParcel(config); } + + /** + * Verify that a default/empty configuration is invalid. + * + * @throws Exception + */ + @Test + public void validateDefaultConfig() throws Exception { + PasspointConfiguration config = new PasspointConfiguration(); + assertFalse(config.validate()); + } + + /** + * Verify that a configuration without Credential is invalid. + * + * @throws Exception + */ + @Test + public void validateConfigWithoutCredential() throws Exception { + PasspointConfiguration config = new PasspointConfiguration(); + config.homeSp = createHomeSp(); + assertFalse(config.validate()); + } + + /** + * Verify that a a configuration without HomeSP is invalid. + * + * @throws Exception + */ + @Test + public void validateConfigWithoutHomeSp() throws Exception { + PasspointConfiguration config = new PasspointConfiguration(); + config.credential = createCredential(); + assertFalse(config.validate()); + } + + /** + * Verify a valid configuration. + * + * @throws Exception + */ + @Test + public void validateValidConfig() throws Exception { + PasspointConfiguration config = new PasspointConfiguration(); + config.homeSp = createHomeSp(); + config.credential = createCredential(); + assertTrue(config.validate()); + } }
\ No newline at end of file diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java index 68ac4efa214f..223aa5231b36 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java @@ -16,14 +16,19 @@ package android.net.wifi.hotspot2.pps; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import android.net.wifi.EAPConstants; import android.net.wifi.FakeKeys; import android.os.Parcel; import android.test.suitebuilder.annotation.SmallTest; +import android.util.Log; +import java.security.MessageDigest; import java.security.PrivateKey; import java.security.cert.X509Certificate; +import java.util.Arrays; import org.junit.Test; @@ -52,15 +57,15 @@ public class CredentialTest { private static Credential createCredentialWithCertificateCredential() { Credential.CertificateCredential certCred = new Credential.CertificateCredential(); certCred.certType = "x509v3"; - certCred.certSha256FingerPrint = new byte[256]; + certCred.certSha256FingerPrint = new byte[32]; return createCredential(null, certCred, null, FakeKeys.CA_CERT0, new X509Certificate[] {FakeKeys.CLIENT_CERT}, FakeKeys.RSA_KEY1); } private static Credential createCredentialWithSimCredential() { Credential.SimCredential simCred = new Credential.SimCredential(); - simCred.imsi = "imsi"; - simCred.eapType = 1; + simCred.imsi = "1234*"; + simCred.eapType = EAPConstants.EAP_SIM; return createCredential(null, null, simCred, null, null, null); } @@ -68,7 +73,7 @@ public class CredentialTest { Credential.UserCredential userCred = new Credential.UserCredential(); userCred.username = "username"; userCred.password = "password"; - userCred.eapType = 1; + userCred.eapType = EAPConstants.EAP_TTLS; userCred.nonEapInnerMethod = "MS-CHAP"; return createCredential(userCred, null, null, FakeKeys.CA_CERT0, new X509Certificate[] {FakeKeys.CLIENT_CERT}, FakeKeys.RSA_KEY1); @@ -83,23 +88,386 @@ public class CredentialTest { assertTrue(readCred.equals(writeCred)); } + /** + * Verify parcel read/write for a default/empty credential. + * + * @throws Exception + */ @Test public void verifyParcelWithDefault() throws Exception { verifyParcel(new Credential()); } + /** + * Verify parcel read/write for a certificate credential. + * + * @throws Exception + */ @Test public void verifyParcelWithCertificateCredential() throws Exception { verifyParcel(createCredentialWithCertificateCredential()); } + /** + * Verify parcel read/write for a SIM credential. + * + * @throws Exception + */ @Test public void verifyParcelWithSimCredential() throws Exception { verifyParcel(createCredentialWithSimCredential()); } + /** + * Verify parcel read/write for an user credential. + * + * @throws Exception + */ @Test public void verifyParcelWithUserCredential() throws Exception { verifyParcel(createCredentialWithUserCredential()); } -} + + /** + * Verify a valid user credential. + * @throws Exception + */ + @Test + public void validateUserCredential() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + cred.userCredential = new Credential.UserCredential(); + cred.userCredential.username = "username"; + cred.userCredential.password = "password"; + cred.userCredential.eapType = EAPConstants.EAP_TTLS; + cred.userCredential.nonEapInnerMethod = "MS-CHAP"; + cred.caCertificate = FakeKeys.CA_CERT0; + assertTrue(cred.validate()); + } + + /** + * Verify that an user credential without CA Certificate is invalid. + * + * @throws Exception + */ + @Test + public void validateUserCredentialWithoutCaCert() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + cred.userCredential = new Credential.UserCredential(); + cred.userCredential.username = "username"; + cred.userCredential.password = "password"; + cred.userCredential.eapType = EAPConstants.EAP_TTLS; + cred.userCredential.nonEapInnerMethod = "MS-CHAP"; + assertFalse(cred.validate()); + } + + /** + * Verify that an user credential with EAP type other than EAP-TTLS is invalid. + * + * @throws Exception + */ + @Test + public void validateUserCredentialWithEapTls() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + cred.userCredential = new Credential.UserCredential(); + cred.userCredential.username = "username"; + cred.userCredential.password = "password"; + cred.userCredential.eapType = EAPConstants.EAP_TLS; + cred.userCredential.nonEapInnerMethod = "MS-CHAP"; + cred.caCertificate = FakeKeys.CA_CERT0; + assertFalse(cred.validate()); + } + + + /** + * Verify that an user credential without realm is invalid. + * + * @throws Exception + */ + @Test + public void validateUserCredentialWithoutRealm() throws Exception { + Credential cred = new Credential(); + cred.userCredential = new Credential.UserCredential(); + cred.userCredential.username = "username"; + cred.userCredential.password = "password"; + cred.userCredential.eapType = EAPConstants.EAP_TTLS; + cred.userCredential.nonEapInnerMethod = "MS-CHAP"; + cred.caCertificate = FakeKeys.CA_CERT0; + assertFalse(cred.validate()); + } + + /** + * Verify that an user credential without username is invalid. + * + * @throws Exception + */ + @Test + public void validateUserCredentialWithoutUsername() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + cred.userCredential = new Credential.UserCredential(); + cred.userCredential.password = "password"; + cred.userCredential.eapType = EAPConstants.EAP_TTLS; + cred.userCredential.nonEapInnerMethod = "MS-CHAP"; + cred.caCertificate = FakeKeys.CA_CERT0; + assertFalse(cred.validate()); + } + + /** + * Verify that an user credential without password is invalid. + * + * @throws Exception + */ + @Test + public void validateUserCredentialWithoutPassword() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + cred.userCredential = new Credential.UserCredential(); + cred.userCredential.username = "username"; + cred.userCredential.eapType = EAPConstants.EAP_TTLS; + cred.userCredential.nonEapInnerMethod = "MS-CHAP"; + cred.caCertificate = FakeKeys.CA_CERT0; + assertFalse(cred.validate()); + } + + /** + * Verify that an user credential without auth methoh (non-EAP inner method) is invalid. + * + * @throws Exception + */ + @Test + public void validateUserCredentialWithoutAuthMethod() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + cred.userCredential = new Credential.UserCredential(); + cred.userCredential.username = "username"; + cred.userCredential.password = "password"; + cred.userCredential.eapType = EAPConstants.EAP_TTLS; + cred.caCertificate = FakeKeys.CA_CERT0; + assertFalse(cred.validate()); + } + + /** + * Verify a certificate credential. CA Certificate, client certificate chain, + * and client private key are all required. Also the digest for client + * certificate must match the fingerprint specified in the certificate credential. + * + * @throws Exception + */ + @Test + public void validateCertCredential() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup certificate credential. + cred.certCredential = new Credential.CertificateCredential(); + cred.certCredential.certType = "x509v3"; + cred.certCredential.certSha256FingerPrint = + MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()); + // Setup certificates and private key. + cred.caCertificate = FakeKeys.CA_CERT0; + cred.clientCertificateChain = new X509Certificate[] {FakeKeys.CLIENT_CERT}; + cred.clientPrivateKey = FakeKeys.RSA_KEY1; + assertTrue(cred.validate()); + } + + /** + * Verify that an certificate credential without CA Certificate is invalid. + * + * @throws Exception + */ + public void validateCertCredentialWithoutCaCert() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup certificate credential. + cred.certCredential = new Credential.CertificateCredential(); + cred.certCredential.certType = "x509v3"; + cred.certCredential.certSha256FingerPrint = + MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()); + // Setup certificates and private key. + cred.clientCertificateChain = new X509Certificate[] {FakeKeys.CLIENT_CERT}; + cred.clientPrivateKey = FakeKeys.RSA_KEY1; + assertFalse(cred.validate()); + } + + /** + * Verify that a certificate credential without client certificate chain is invalid. + * + * @throws Exception + */ + @Test + public void validateCertCredentialWithoutClientCertChain() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup certificate credential. + cred.certCredential = new Credential.CertificateCredential(); + cred.certCredential.certType = "x509v3"; + cred.certCredential.certSha256FingerPrint = + MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()); + // Setup certificates and private key. + cred.caCertificate = FakeKeys.CA_CERT0; + cred.clientPrivateKey = FakeKeys.RSA_KEY1; + assertFalse(cred.validate()); + } + + /** + * Verify that a certificate credential without client private key is invalid. + * + * @throws Exception + */ + @Test + public void validateCertCredentialWithoutClientPrivateKey() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup certificate credential. + cred.certCredential = new Credential.CertificateCredential(); + cred.certCredential.certType = "x509v3"; + cred.certCredential.certSha256FingerPrint = + MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()); + // Setup certificates and private key. + cred.caCertificate = FakeKeys.CA_CERT0; + cred.clientCertificateChain = new X509Certificate[] {FakeKeys.CLIENT_CERT}; + assertFalse(cred.validate()); + } + + /** + * Verify that a certificate credential with mismatch client certificate fingerprint + * is invalid. + * + * @throws Exception + */ + @Test + public void validateCertCredentialWithMismatchFingerprint() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup certificate credential. + cred.certCredential = new Credential.CertificateCredential(); + cred.certCredential.certType = "x509v3"; + cred.certCredential.certSha256FingerPrint = new byte[32]; + Arrays.fill(cred.certCredential.certSha256FingerPrint, (byte)0); + // Setup certificates and private key. + cred.caCertificate = FakeKeys.CA_CERT0; + cred.clientCertificateChain = new X509Certificate[] {FakeKeys.CLIENT_CERT}; + cred.clientPrivateKey = FakeKeys.RSA_KEY1; + assertFalse(cred.validate()); + } + + /** + * Verify a SIM credential using EAP-SIM. + * + * @throws Exception + */ + @Test + public void validateSimCredentialWithEapSim() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup SIM credential. + cred.simCredential = new Credential.SimCredential(); + cred.simCredential.imsi = "1234*"; + cred.simCredential.eapType = EAPConstants.EAP_SIM; + assertTrue(cred.validate()); + } + + /** + * Verify a SIM credential using EAP-AKA. + * + * @throws Exception + */ + @Test + public void validateSimCredentialWithEapAka() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup SIM credential. + cred.simCredential = new Credential.SimCredential(); + cred.simCredential.imsi = "1234*"; + cred.simCredential.eapType = EAPConstants.EAP_AKA; + assertTrue(cred.validate()); + } + + /** + * Verify a SIM credential using EAP-AKA-PRIME. + * + * @throws Exception + */ + @Test + public void validateSimCredentialWithEapAkaPrime() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup SIM credential. + cred.simCredential = new Credential.SimCredential(); + cred.simCredential.imsi = "1234*"; + cred.simCredential.eapType = EAPConstants.EAP_AKA_PRIME; + assertTrue(cred.validate()); + } + + /** + * Verify that a SIM credential without IMSI is invalid. + * + * @throws Exception + */ + @Test + public void validateSimCredentialWithoutIMSI() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup SIM credential. + cred.simCredential = new Credential.SimCredential(); + cred.simCredential.eapType = EAPConstants.EAP_SIM; + assertFalse(cred.validate()); + } + + /** + * Verify that a SIM credential with an invalid IMSI is invalid. + * + * @throws Exception + */ + @Test + public void validateSimCredentialWithInvalidIMSI() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup SIM credential. + cred.simCredential = new Credential.SimCredential(); + cred.simCredential.imsi = "dummy"; + cred.simCredential.eapType = EAPConstants.EAP_SIM; + assertFalse(cred.validate()); + } + + /** + * Verify that a SIM credential with invalid EAP type is invalid. + * + * @throws Exception + */ + @Test + public void validateSimCredentialWithEapTls() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup SIM credential. + cred.simCredential = new Credential.SimCredential(); + cred.simCredential.imsi = "1234*"; + cred.simCredential.eapType = EAPConstants.EAP_TLS; + assertFalse(cred.validate()); + } + + /** + * Verify that a credential contained both an user and a SIM credential is invalid. + * + * @throws Exception + */ + @Test + public void validateCredentialWithUserAndSimCredential() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup user credential with EAP-TTLS. + cred.userCredential = new Credential.UserCredential(); + cred.userCredential.username = "username"; + cred.userCredential.password = "password"; + cred.userCredential.eapType = EAPConstants.EAP_TTLS; + cred.userCredential.nonEapInnerMethod = "MS-CHAP"; + cred.caCertificate = FakeKeys.CA_CERT0; + // Setup SIM credential. + cred.simCredential = new Credential.SimCredential(); + cred.simCredential.imsi = "1234*"; + cred.simCredential.eapType = EAPConstants.EAP_SIM; + assertFalse(cred.validate()); + } +}
\ No newline at end of file diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java index 0d2da6404e7f..fff1477e833b 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java @@ -16,13 +16,12 @@ package android.net.wifi.hotspot2.pps; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.os.Parcel; import android.test.suitebuilder.annotation.SmallTest; -import java.util.HashMap; - import org.junit.Test; /** @@ -47,13 +46,76 @@ public class HomeSPTest { assertTrue(readHomeSp.equals(writeHomeSp)); } + /** + * Verify parcel read/write for an empty HomeSP. + * + * @throws Exception + */ @Test public void verifyParcelWithEmptyHomeSP() throws Exception { verifyParcel(new HomeSP()); } + /** + * Verify parcel read/write for a valid HomeSP. + * + * @throws Exception + */ @Test public void verifyParcelWithValidHomeSP() throws Exception { verifyParcel(createHomeSp()); } + + /** + * Verify that a HomeSP is valid when both FQDN and Friendly Name + * are provided. + * + * @throws Exception + */ + @Test + public void validateValidHomeSP() throws Exception { + HomeSP homeSp = new HomeSP(); + homeSp.fqdn = "fqdn"; + homeSp.friendlyName = "friendly name"; + assertTrue(homeSp.validate()); + } + + /** + * Verify that a HomeSP is not valid when FQDN is not provided + * + * @throws Exception + */ + @Test + public void validateHomeSpWithoutFqdn() throws Exception { + HomeSP homeSp = new HomeSP(); + homeSp.friendlyName = "friendly name"; + assertFalse(homeSp.validate()); + } + + /** + * Verify that a HomeSP is not valid when Friendly Name is not provided + * + * @throws Exception + */ + @Test + public void validateHomeSpWithoutFriendlyName() throws Exception { + HomeSP homeSp = new HomeSP(); + homeSp.fqdn = "fqdn"; + assertFalse(homeSp.validate()); + } + + /** + * Verify that a HomeSP is valid when the optional Roaming Consortium OIs are + * provided. + * + * @throws Exception + */ + @Test + public void validateHomeSpWithRoamingConsoritums() throws Exception { + HomeSP homeSp = new HomeSP(); + homeSp.fqdn = "fqdn"; + homeSp.friendlyName = "friendly name"; + homeSp.roamingConsortiumOIs = new long[] {0x55, 0x66}; + assertTrue(homeSp.validate()); + } } |