1package com.android.server.wifi.hotspot2.pps; 2 3import android.net.wifi.WifiEnterpriseConfig; 4import android.security.Credentials; 5import android.security.KeyStore; 6import android.text.TextUtils; 7import android.util.Base64; 8import android.util.Log; 9 10import com.android.server.wifi.IMSIParameter; 11import com.android.server.wifi.anqp.eap.EAP; 12import com.android.server.wifi.anqp.eap.EAPMethod; 13import com.android.server.wifi.anqp.eap.NonEAPInnerAuth; 14import com.android.server.wifi.hotspot2.Utils; 15import com.android.server.wifi.hotspot2.omadm.OMAException; 16 17import java.io.IOException; 18import java.nio.charset.StandardCharsets; 19import java.security.GeneralSecurityException; 20import java.security.MessageDigest; 21import java.util.Arrays; 22 23public class Credential { 24 public enum CertType {IEEE, x509v3} 25 26 public static final String CertTypeX509 = "x509v3"; 27 public static final String CertTypeIEEE = "802.1ar"; 28 29 private final long mCtime; 30 private final long mExpTime; 31 private final String mRealm; 32 private final boolean mCheckAAACert; 33 34 private final String mUserName; 35 private final String mPassword; 36 private final boolean mDisregardPassword; 37 private final boolean mMachineManaged; 38 private final String mSTokenApp; 39 private final boolean mShare; 40 private final EAPMethod mEAPMethod; 41 42 private final CertType mCertType; 43 private final byte[] mFingerPrint; 44 45 private final IMSIParameter mImsi; 46 47 public Credential(long ctime, long expTime, String realm, boolean checkAAACert, 48 EAPMethod eapMethod, String userName, String password, 49 boolean machineManaged, String stApp, boolean share) { 50 mCtime = ctime; 51 mExpTime = expTime; 52 mRealm = realm; 53 mCheckAAACert = checkAAACert; 54 mEAPMethod = eapMethod; 55 mUserName = userName; 56 57 if (!TextUtils.isEmpty(password)) { 58 byte[] pwOctets = Base64.decode(password, Base64.DEFAULT); 59 mPassword = new String(pwOctets, StandardCharsets.UTF_8); 60 } else { 61 mPassword = null; 62 } 63 mDisregardPassword = false; 64 65 mMachineManaged = machineManaged; 66 mSTokenApp = stApp; 67 mShare = share; 68 69 mCertType = null; 70 mFingerPrint = null; 71 72 mImsi = null; 73 } 74 75 public Credential(long ctime, long expTime, String realm, boolean checkAAACert, 76 EAPMethod eapMethod, Credential.CertType certType, byte[] fingerPrint) { 77 mCtime = ctime; 78 mExpTime = expTime; 79 mRealm = realm; 80 mCheckAAACert = checkAAACert; 81 mEAPMethod = eapMethod; 82 mCertType = certType; 83 mFingerPrint = fingerPrint; 84 85 mUserName = null; 86 mPassword = null; 87 mDisregardPassword = false; 88 mMachineManaged = false; 89 mSTokenApp = null; 90 mShare = false; 91 92 mImsi = null; 93 } 94 95 public Credential(long ctime, long expTime, String realm, boolean checkAAACert, 96 EAPMethod eapMethod, IMSIParameter imsi) { 97 mCtime = ctime; 98 mExpTime = expTime; 99 mRealm = realm; 100 mCheckAAACert = checkAAACert; 101 mEAPMethod = eapMethod; 102 mImsi = imsi; 103 104 mCertType = null; 105 mFingerPrint = null; 106 107 mUserName = null; 108 mPassword = null; 109 mDisregardPassword = false; 110 mMachineManaged = false; 111 mSTokenApp = null; 112 mShare = false; 113 } 114 115 public Credential(Credential other, String password) { 116 mCtime = other.mCtime; 117 mExpTime = other.mExpTime; 118 mRealm = other.mRealm; 119 mCheckAAACert = other.mCheckAAACert; 120 mUserName = other.mUserName; 121 mPassword = password; 122 mDisregardPassword = other.mDisregardPassword; 123 mMachineManaged = other.mMachineManaged; 124 mSTokenApp = other.mSTokenApp; 125 mShare = other.mShare; 126 mEAPMethod = other.mEAPMethod; 127 mCertType = other.mCertType; 128 mFingerPrint = other.mFingerPrint; 129 mImsi = other.mImsi; 130 } 131 132 public Credential(WifiEnterpriseConfig enterpriseConfig, KeyStore keyStore, boolean update) 133 throws IOException { 134 mCtime = Utils.UNSET_TIME; 135 mExpTime = Utils.UNSET_TIME; 136 mRealm = enterpriseConfig.getRealm(); 137 mCheckAAACert = false; 138 mEAPMethod = mapEapMethod(enterpriseConfig.getEapMethod(), 139 enterpriseConfig.getPhase2Method()); 140 mCertType = mEAPMethod.getEAPMethodID() == EAP.EAPMethodID.EAP_TLS ? CertType.x509v3 : null; 141 byte[] fingerPrint; 142 143 if (enterpriseConfig.getClientCertificate() != null) { 144 // !!! Not sure this will be true in any practical instances: 145 try { 146 MessageDigest digester = MessageDigest.getInstance("SHA-256"); 147 fingerPrint = digester.digest(enterpriseConfig.getClientCertificate().getEncoded()); 148 } catch (GeneralSecurityException gse) { 149 Log.e(Utils.hs2LogTag(getClass()), 150 "Failed to generate certificate fingerprint: " + gse); 151 fingerPrint = null; 152 } 153 } else if (enterpriseConfig.getClientCertificateAlias() != null) { 154 String alias = enterpriseConfig.getClientCertificateAlias(); 155 byte[] octets = keyStore.get(Credentials.USER_CERTIFICATE + alias); 156 if (octets != null) { 157 try { 158 MessageDigest digester = MessageDigest.getInstance("SHA-256"); 159 fingerPrint = digester.digest(octets); 160 } catch (GeneralSecurityException gse) { 161 Log.e(Utils.hs2LogTag(getClass()), "Failed to construct digest: " + gse); 162 fingerPrint = null; 163 } 164 } else // !!! The current alias is *not* derived from the fingerprint... 165 { 166 try { 167 fingerPrint = Base64.decode(enterpriseConfig.getClientCertificateAlias(), 168 Base64.DEFAULT); 169 } catch (IllegalArgumentException ie) { 170 Log.e(Utils.hs2LogTag(getClass()), "Bad base 64 alias"); 171 fingerPrint = null; 172 } 173 } 174 } else { 175 fingerPrint = null; 176 } 177 mFingerPrint = fingerPrint; 178 String imsi = enterpriseConfig.getPlmn(); 179 mImsi = imsi == null || imsi.length() == 0 ? null : new IMSIParameter(imsi); 180 mUserName = enterpriseConfig.getIdentity(); 181 mPassword = enterpriseConfig.getPassword(); 182 mDisregardPassword = update && mPassword.length() < 2; 183 mMachineManaged = false; 184 mSTokenApp = null; 185 mShare = false; 186 } 187 188 public static CertType mapCertType(String certType) throws OMAException { 189 if (certType.equalsIgnoreCase(CertTypeX509)) { 190 return CertType.x509v3; 191 } else if (certType.equalsIgnoreCase(CertTypeIEEE)) { 192 return CertType.IEEE; 193 } else { 194 throw new OMAException("Invalid cert type: '" + certType + "'"); 195 } 196 } 197 198 private static EAPMethod mapEapMethod(int eapMethod, int phase2Method) throws IOException { 199 switch (eapMethod) { 200 case WifiEnterpriseConfig.Eap.TLS: 201 return new EAPMethod(EAP.EAPMethodID.EAP_TLS, null); 202 case WifiEnterpriseConfig.Eap.TTLS: 203 /* keep this table in sync with WifiEnterpriseConfig.Phase2 enum */ 204 NonEAPInnerAuth inner; 205 switch (phase2Method) { 206 case WifiEnterpriseConfig.Phase2.PAP: 207 inner = new NonEAPInnerAuth(NonEAPInnerAuth.NonEAPType.PAP); 208 break; 209 case WifiEnterpriseConfig.Phase2.MSCHAP: 210 inner = new NonEAPInnerAuth(NonEAPInnerAuth.NonEAPType.MSCHAP); 211 break; 212 case WifiEnterpriseConfig.Phase2.MSCHAPV2: 213 inner = new NonEAPInnerAuth(NonEAPInnerAuth.NonEAPType.MSCHAPv2); 214 break; 215 default: 216 throw new IOException("TTLS phase2 method " + 217 phase2Method + " not valid for Passpoint"); 218 } 219 return new EAPMethod(EAP.EAPMethodID.EAP_TTLS, inner); 220 case WifiEnterpriseConfig.Eap.SIM: 221 return new EAPMethod(EAP.EAPMethodID.EAP_SIM, null); 222 case WifiEnterpriseConfig.Eap.AKA: 223 return new EAPMethod(EAP.EAPMethodID.EAP_AKA, null); 224 case WifiEnterpriseConfig.Eap.AKA_PRIME: 225 return new EAPMethod(EAP.EAPMethodID.EAP_AKAPrim, null); 226 default: 227 String methodName; 228 if (eapMethod >= 0 && eapMethod < WifiEnterpriseConfig.Eap.strings.length) { 229 methodName = WifiEnterpriseConfig.Eap.strings[eapMethod]; 230 } else { 231 methodName = Integer.toString(eapMethod); 232 } 233 throw new IOException("EAP method id " + methodName + " is not valid for Passpoint"); 234 } 235 } 236 237 public EAPMethod getEAPMethod() { 238 return mEAPMethod; 239 } 240 241 public String getRealm() { 242 return mRealm; 243 } 244 245 public IMSIParameter getImsi() { 246 return mImsi; 247 } 248 249 public String getUserName() { 250 return mUserName; 251 } 252 253 public String getPassword() { 254 return mPassword; 255 } 256 257 public boolean hasDisregardPassword() { 258 return mDisregardPassword; 259 } 260 261 public CertType getCertType() { 262 return mCertType; 263 } 264 265 public byte[] getFingerPrint() { 266 return mFingerPrint; 267 } 268 269 public long getCtime() { 270 return mCtime; 271 } 272 273 public long getExpTime() { 274 return mExpTime; 275 } 276 277 @Override 278 public boolean equals(Object o) { 279 if (this == o) return true; 280 if (o == null || getClass() != o.getClass()) return false; 281 282 Credential that = (Credential) o; 283 284 if (mCheckAAACert != that.mCheckAAACert) return false; 285 if (mCtime != that.mCtime) return false; 286 if (mExpTime != that.mExpTime) return false; 287 if (mMachineManaged != that.mMachineManaged) return false; 288 if (mShare != that.mShare) return false; 289 if (mCertType != that.mCertType) return false; 290 if (!mEAPMethod.equals(that.mEAPMethod)) return false; 291 if (!Arrays.equals(mFingerPrint, that.mFingerPrint)) return false; 292 if (!safeEquals(mImsi, that.mImsi)) { 293 return false; 294 } 295 296 if (!mDisregardPassword && !safeEquals(mPassword, that.mPassword)) { 297 return false; 298 } 299 300 if (!mRealm.equals(that.mRealm)) return false; 301 if (!safeEquals(mSTokenApp, that.mSTokenApp)) { 302 return false; 303 } 304 if (!safeEquals(mUserName, that.mUserName)) { 305 return false; 306 } 307 308 return true; 309 } 310 311 private static boolean safeEquals(Object s1, Object s2) { 312 if (s1 == null) { 313 return s2 == null; 314 } 315 else { 316 return s2 != null && s1.equals(s2); 317 } 318 } 319 320 @Override 321 public int hashCode() { 322 int result = (int) (mCtime ^ (mCtime >>> 32)); 323 result = 31 * result + (int) (mExpTime ^ (mExpTime >>> 32)); 324 result = 31 * result + mRealm.hashCode(); 325 result = 31 * result + (mCheckAAACert ? 1 : 0); 326 result = 31 * result + (mUserName != null ? mUserName.hashCode() : 0); 327 result = 31 * result + (mPassword != null ? mPassword.hashCode() : 0); 328 result = 31 * result + (mMachineManaged ? 1 : 0); 329 result = 31 * result + (mSTokenApp != null ? mSTokenApp.hashCode() : 0); 330 result = 31 * result + (mShare ? 1 : 0); 331 result = 31 * result + mEAPMethod.hashCode(); 332 result = 31 * result + (mCertType != null ? mCertType.hashCode() : 0); 333 result = 31 * result + (mFingerPrint != null ? Arrays.hashCode(mFingerPrint) : 0); 334 result = 31 * result + (mImsi != null ? mImsi.hashCode() : 0); 335 return result; 336 } 337 338 @Override 339 public String toString() { 340 return "Credential{" + 341 "mCtime=" + Utils.toUTCString(mCtime) + 342 ", mExpTime=" + Utils.toUTCString(mExpTime) + 343 ", mRealm='" + mRealm + '\'' + 344 ", mCheckAAACert=" + mCheckAAACert + 345 ", mUserName='" + mUserName + '\'' + 346 ", mPassword='" + mPassword + '\'' + 347 ", mDisregardPassword=" + mDisregardPassword + 348 ", mMachineManaged=" + mMachineManaged + 349 ", mSTokenApp='" + mSTokenApp + '\'' + 350 ", mShare=" + mShare + 351 ", mEAPMethod=" + mEAPMethod + 352 ", mCertType=" + mCertType + 353 ", mFingerPrint=" + Utils.toHexString(mFingerPrint) + 354 ", mImsi='" + mImsi + '\'' + 355 '}'; 356 } 357} 358