1package com.android.anqp.eap; 2 3import com.android.anqp.Constants; 4import com.android.hotspot2.AuthMatch; 5 6import java.net.ProtocolException; 7import java.nio.ByteBuffer; 8import java.nio.ByteOrder; 9import java.util.Collections; 10import java.util.EnumMap; 11import java.util.HashMap; 12import java.util.HashSet; 13import java.util.Map; 14import java.util.Set; 15 16/** 17 * An EAP Method, part of the NAI Realm ANQP element, specified in 18 * IEEE802.11-2012 section 8.4.4.10, figure 8-420 19 */ 20public class EAPMethod { 21 private final EAP.EAPMethodID mEAPMethodID; 22 private final Map<EAP.AuthInfoID, Set<AuthParam>> mAuthParams; 23 24 public EAPMethod(ByteBuffer payload) throws ProtocolException { 25 if (payload.remaining() < 3) { 26 throw new ProtocolException("Runt EAP Method: " + payload.remaining()); 27 } 28 29 int length = payload.get() & Constants.BYTE_MASK; 30 int methodID = payload.get() & Constants.BYTE_MASK; 31 int count = payload.get() & Constants.BYTE_MASK; 32 33 mEAPMethodID = EAP.mapEAPMethod(methodID); 34 mAuthParams = new EnumMap<>(EAP.AuthInfoID.class); 35 36 int realCount = 0; 37 38 ByteBuffer paramPayload = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN); 39 paramPayload.limit(paramPayload.position() + length - 2); 40 payload.position(payload.position() + length - 2); 41 while (paramPayload.hasRemaining()) { 42 int id = paramPayload.get() & Constants.BYTE_MASK; 43 44 EAP.AuthInfoID authInfoID = EAP.mapAuthMethod(id); 45 if (authInfoID == null) { 46 throw new ProtocolException("Unknown auth parameter ID: " + id); 47 } 48 49 int len = paramPayload.get() & Constants.BYTE_MASK; 50 if (len == 0 || len > paramPayload.remaining()) { 51 throw new ProtocolException("Bad auth method length: " + len); 52 } 53 54 switch (authInfoID) { 55 case ExpandedEAPMethod: 56 addAuthParam(new ExpandedEAPMethod(authInfoID, len, paramPayload)); 57 break; 58 case NonEAPInnerAuthType: 59 addAuthParam(new NonEAPInnerAuth(len, paramPayload)); 60 break; 61 case InnerAuthEAPMethodType: 62 addAuthParam(new InnerAuthEAP(len, paramPayload)); 63 break; 64 case ExpandedInnerEAPMethod: 65 addAuthParam(new ExpandedEAPMethod(authInfoID, len, paramPayload)); 66 break; 67 case CredentialType: 68 addAuthParam(new Credential(authInfoID, len, paramPayload)); 69 break; 70 case TunneledEAPMethodCredType: 71 addAuthParam(new Credential(authInfoID, len, paramPayload)); 72 break; 73 case VendorSpecific: 74 addAuthParam(new VendorSpecificAuth(len, paramPayload)); 75 break; 76 } 77 78 realCount++; 79 } 80 if (realCount != count) 81 throw new ProtocolException("Invalid parameter count: " + realCount + 82 ", expected " + count); 83 } 84 85 public EAPMethod(EAP.EAPMethodID eapMethodID, AuthParam authParam) { 86 mEAPMethodID = eapMethodID; 87 mAuthParams = new HashMap<>(1); 88 if (authParam != null) { 89 Set<AuthParam> authParams = new HashSet<>(); 90 authParams.add(authParam); 91 mAuthParams.put(authParam.getAuthInfoID(), authParams); 92 } 93 } 94 95 private void addAuthParam(AuthParam param) { 96 Set<AuthParam> authParams = mAuthParams.get(param.getAuthInfoID()); 97 if (authParams == null) { 98 authParams = new HashSet<>(); 99 mAuthParams.put(param.getAuthInfoID(), authParams); 100 } 101 authParams.add(param); 102 } 103 104 public Map<EAP.AuthInfoID, Set<AuthParam>> getAuthParams() { 105 return Collections.unmodifiableMap(mAuthParams); 106 } 107 108 public EAP.EAPMethodID getEAPMethodID() { 109 return mEAPMethodID; 110 } 111 112 public int match(com.android.hotspot2.pps.Credential credential) { 113 114 EAPMethod credMethod = credential.getEAPMethod(); 115 if (mEAPMethodID != credMethod.getEAPMethodID()) { 116 return AuthMatch.None; 117 } 118 119 switch (mEAPMethodID) { 120 case EAP_TTLS: 121 if (mAuthParams.isEmpty()) { 122 return AuthMatch.Method; 123 } 124 int paramCount = 0; 125 for (Map.Entry<EAP.AuthInfoID, Set<AuthParam>> entry : 126 credMethod.getAuthParams().entrySet()) { 127 Set<AuthParam> params = mAuthParams.get(entry.getKey()); 128 if (params == null) { 129 continue; 130 } 131 132 if (!Collections.disjoint(params, entry.getValue())) { 133 return AuthMatch.MethodParam; 134 } 135 paramCount += params.size(); 136 } 137 return paramCount > 0 ? AuthMatch.None : AuthMatch.Method; 138 case EAP_TLS: 139 return AuthMatch.MethodParam; 140 case EAP_SIM: 141 case EAP_AKA: 142 case EAP_AKAPrim: 143 return AuthMatch.Method; 144 default: 145 return AuthMatch.Method; 146 } 147 } 148 149 public AuthParam getAuthParam() { 150 if (mAuthParams.isEmpty()) { 151 return null; 152 } 153 Set<AuthParam> params = mAuthParams.values().iterator().next(); 154 if (params.isEmpty()) { 155 return null; 156 } 157 return params.iterator().next(); 158 } 159 160 @Override 161 public boolean equals(Object thatObject) { 162 if (this == thatObject) { 163 return true; 164 } 165 else if (thatObject == null || getClass() != thatObject.getClass()) { 166 return false; 167 } 168 169 EAPMethod that = (EAPMethod) thatObject; 170 return mEAPMethodID == that.mEAPMethodID && mAuthParams.equals(that.mAuthParams); 171 } 172 173 @Override 174 public int hashCode() { 175 int result = mEAPMethodID.hashCode(); 176 result = 31 * result + mAuthParams.hashCode(); 177 return result; 178 } 179 180 @Override 181 public String toString() { 182 StringBuilder sb = new StringBuilder(); 183 sb.append("EAP Method ").append(mEAPMethodID).append('\n'); 184 for (Set<AuthParam> paramSet : mAuthParams.values()) { 185 for (AuthParam param : paramSet) { 186 sb.append(" ").append(param.toString()); 187 } 188 } 189 return sb.toString(); 190 } 191} 192