package com.android.anqp.eap; import com.android.anqp.Constants; import com.android.hotspot2.AuthMatch; import java.net.ProtocolException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * An EAP Method, part of the NAI Realm ANQP element, specified in * IEEE802.11-2012 section 8.4.4.10, figure 8-420 */ public class EAPMethod { private final EAP.EAPMethodID mEAPMethodID; private final Map> mAuthParams; public EAPMethod(ByteBuffer payload) throws ProtocolException { if (payload.remaining() < 3) { throw new ProtocolException("Runt EAP Method: " + payload.remaining()); } int length = payload.get() & Constants.BYTE_MASK; int methodID = payload.get() & Constants.BYTE_MASK; int count = payload.get() & Constants.BYTE_MASK; mEAPMethodID = EAP.mapEAPMethod(methodID); mAuthParams = new EnumMap<>(EAP.AuthInfoID.class); int realCount = 0; ByteBuffer paramPayload = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN); paramPayload.limit(paramPayload.position() + length - 2); payload.position(payload.position() + length - 2); while (paramPayload.hasRemaining()) { int id = paramPayload.get() & Constants.BYTE_MASK; EAP.AuthInfoID authInfoID = EAP.mapAuthMethod(id); if (authInfoID == null) { throw new ProtocolException("Unknown auth parameter ID: " + id); } int len = paramPayload.get() & Constants.BYTE_MASK; if (len == 0 || len > paramPayload.remaining()) { throw new ProtocolException("Bad auth method length: " + len); } switch (authInfoID) { case ExpandedEAPMethod: addAuthParam(new ExpandedEAPMethod(authInfoID, len, paramPayload)); break; case NonEAPInnerAuthType: addAuthParam(new NonEAPInnerAuth(len, paramPayload)); break; case InnerAuthEAPMethodType: addAuthParam(new InnerAuthEAP(len, paramPayload)); break; case ExpandedInnerEAPMethod: addAuthParam(new ExpandedEAPMethod(authInfoID, len, paramPayload)); break; case CredentialType: addAuthParam(new Credential(authInfoID, len, paramPayload)); break; case TunneledEAPMethodCredType: addAuthParam(new Credential(authInfoID, len, paramPayload)); break; case VendorSpecific: addAuthParam(new VendorSpecificAuth(len, paramPayload)); break; } realCount++; } if (realCount != count) throw new ProtocolException("Invalid parameter count: " + realCount + ", expected " + count); } public EAPMethod(EAP.EAPMethodID eapMethodID, AuthParam authParam) { mEAPMethodID = eapMethodID; mAuthParams = new HashMap<>(1); if (authParam != null) { Set authParams = new HashSet<>(); authParams.add(authParam); mAuthParams.put(authParam.getAuthInfoID(), authParams); } } private void addAuthParam(AuthParam param) { Set authParams = mAuthParams.get(param.getAuthInfoID()); if (authParams == null) { authParams = new HashSet<>(); mAuthParams.put(param.getAuthInfoID(), authParams); } authParams.add(param); } public Map> getAuthParams() { return Collections.unmodifiableMap(mAuthParams); } public EAP.EAPMethodID getEAPMethodID() { return mEAPMethodID; } public int match(com.android.hotspot2.pps.Credential credential) { EAPMethod credMethod = credential.getEAPMethod(); if (mEAPMethodID != credMethod.getEAPMethodID()) { return AuthMatch.None; } switch (mEAPMethodID) { case EAP_TTLS: if (mAuthParams.isEmpty()) { return AuthMatch.Method; } int paramCount = 0; for (Map.Entry> entry : credMethod.getAuthParams().entrySet()) { Set params = mAuthParams.get(entry.getKey()); if (params == null) { continue; } if (!Collections.disjoint(params, entry.getValue())) { return AuthMatch.MethodParam; } paramCount += params.size(); } return paramCount > 0 ? AuthMatch.None : AuthMatch.Method; case EAP_TLS: return AuthMatch.MethodParam; case EAP_SIM: case EAP_AKA: case EAP_AKAPrim: return AuthMatch.Method; default: return AuthMatch.Method; } } public AuthParam getAuthParam() { if (mAuthParams.isEmpty()) { return null; } Set params = mAuthParams.values().iterator().next(); if (params.isEmpty()) { return null; } return params.iterator().next(); } @Override public boolean equals(Object thatObject) { if (this == thatObject) { return true; } else if (thatObject == null || getClass() != thatObject.getClass()) { return false; } EAPMethod that = (EAPMethod) thatObject; return mEAPMethodID == that.mEAPMethodID && mAuthParams.equals(that.mAuthParams); } @Override public int hashCode() { int result = mEAPMethodID.hashCode(); result = 31 * result + mAuthParams.hashCode(); return result; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("EAP Method ").append(mEAPMethodID).append('\n'); for (Set paramSet : mAuthParams.values()) { for (AuthParam param : paramSet) { sb.append(" ").append(param.toString()); } } return sb.toString(); } }