1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.net.eap.message; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 22 import java.lang.annotation.Retention; 23 import java.lang.annotation.RetentionPolicy; 24 import java.nio.ByteBuffer; 25 import java.util.Arrays; 26 import java.util.HashMap; 27 import java.util.HashSet; 28 import java.util.Map; 29 import java.util.Objects; 30 import java.util.Set; 31 32 /** 33 * EapData represents the data-bytes of an EAP-Packet. 34 * 35 * <p>EapData objects will always have a Type-value and the Type-Data bytes that follow. 36 * 37 * EapData objects should be parsed from the Type and Type-Data sections of an EAP Packet, as shown 38 * below: 39 * 40 * +-----------------+-----------------+----------------------------------+ 41 * | Code (1B) | Identifier (1B) | Length (2B) | 42 * +-----------------+-----------------+----------------------------------+ 43 * | Type (1B) | Type-Data ... 44 * +-----------------+----- 45 * 46 * @see <a href="https://tools.ietf.org/html/rfc3748#section-4">RFC 3748, Extensible Authentication 47 * Protocol (EAP)</a> 48 */ 49 public class EapData { 50 @Retention(RetentionPolicy.SOURCE) 51 @IntDef({ 52 EAP_IDENTITY, 53 EAP_NOTIFICATION, 54 EAP_NAK, 55 EAP_TYPE_SIM, 56 EAP_TYPE_AKA, 57 EAP_TYPE_MSCHAP_V2, 58 EAP_TYPE_AKA_PRIME, 59 EAP_TYPE_TTLS 60 }) 61 public @interface EapType {} 62 63 @Retention(RetentionPolicy.SOURCE) 64 @IntDef({ 65 EAP_TYPE_SIM, 66 EAP_TYPE_AKA, 67 EAP_TYPE_MSCHAP_V2, 68 EAP_TYPE_AKA_PRIME, 69 EAP_TYPE_TTLS 70 }) 71 public @interface EapMethod {} 72 73 // EAP Type values defined by IANA 74 // https://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml 75 public static final int EAP_IDENTITY = 1; 76 public static final int EAP_NOTIFICATION = 2; 77 public static final int EAP_NAK = 3; 78 // EAP_MD5_CHALLENGE unsupported, allowable based on RFC 3748, Section 5.4 79 public static final int EAP_TYPE_SIM = 18; 80 public static final int EAP_TYPE_TTLS = 21; 81 public static final int EAP_TYPE_AKA = 23; 82 public static final int EAP_TYPE_MSCHAP_V2 = 26; 83 public static final int EAP_TYPE_AKA_PRIME = 50; 84 85 public static final Map<Integer, String> EAP_TYPE_STRING = new HashMap<>(); 86 static { EAP_TYPE_STRING.put(EAP_IDENTITY, "Identity")87 EAP_TYPE_STRING.put(EAP_IDENTITY, "Identity"); EAP_TYPE_STRING.put(EAP_NOTIFICATION, "Notification")88 EAP_TYPE_STRING.put(EAP_NOTIFICATION, "Notification"); EAP_TYPE_STRING.put(EAP_NAK, "Nak")89 EAP_TYPE_STRING.put(EAP_NAK, "Nak"); EAP_TYPE_STRING.put(EAP_TYPE_SIM, "EAP-SIM")90 EAP_TYPE_STRING.put(EAP_TYPE_SIM, "EAP-SIM"); EAP_TYPE_STRING.put(EAP_TYPE_AKA, "EAP-AKA")91 EAP_TYPE_STRING.put(EAP_TYPE_AKA, "EAP-AKA"); EAP_TYPE_STRING.put(EAP_TYPE_MSCHAP_V2, "EAP-MSCHAP-V2")92 EAP_TYPE_STRING.put(EAP_TYPE_MSCHAP_V2, "EAP-MSCHAP-V2"); EAP_TYPE_STRING.put(EAP_TYPE_AKA_PRIME, "EAP-AKA-PRIME")93 EAP_TYPE_STRING.put(EAP_TYPE_AKA_PRIME, "EAP-AKA-PRIME"); EAP_TYPE_STRING.put(EAP_TYPE_TTLS, "EAP-TTLS")94 EAP_TYPE_STRING.put(EAP_TYPE_TTLS, "EAP-TTLS"); 95 } 96 97 private static final Set<Integer> SUPPORTED_TYPES = new HashSet<>(); 98 static { 99 SUPPORTED_TYPES.add(EAP_IDENTITY); 100 SUPPORTED_TYPES.add(EAP_NOTIFICATION); 101 SUPPORTED_TYPES.add(EAP_NAK); 102 103 // supported EAP Method types 104 SUPPORTED_TYPES.add(EAP_TYPE_SIM); 105 SUPPORTED_TYPES.add(EAP_TYPE_AKA); 106 SUPPORTED_TYPES.add(EAP_TYPE_MSCHAP_V2); 107 SUPPORTED_TYPES.add(EAP_TYPE_AKA_PRIME); 108 SUPPORTED_TYPES.add(EAP_TYPE_TTLS); 109 } 110 111 @EapType public final int eapType; 112 public final byte[] eapTypeData; 113 114 public static final EapData NOTIFICATION_DATA = new EapData(EAP_NOTIFICATION, new byte[0]); 115 116 /** 117 * Constructs a new EapData instance. 118 * 119 * @param eapType the {@link EapType} for this EapData instance 120 * @param eapTypeData the Type-Data for this EapData instance 121 * @throws IllegalArgumentException if eapTypeData is null or if 122 * {@link EapData#isSupportedEapType} is false for the given eapType 123 */ EapData(@apType int eapType, @NonNull byte[] eapTypeData)124 public EapData(@EapType int eapType, @NonNull byte[] eapTypeData) { 125 this.eapType = eapType; 126 this.eapTypeData = eapTypeData; 127 128 validate(); 129 } 130 131 /** 132 * Gets the length of this EapData object. 133 * 134 * @return int for the number of bytes this EapData object represents 135 */ getLength()136 public int getLength() { 137 // length of byte-array + 1 (for 1B type field) 138 return eapTypeData.length + 1; 139 } 140 141 /** 142 * Returns whether this instance is equal to the given Object o. 143 * 144 * @param o the Object to be tested for equality 145 * @return true iff this instance is equal to the given o 146 */ 147 @Override equals(Object o)148 public boolean equals(Object o) { 149 if (this == o) { 150 return true; 151 } 152 if (o == null || !(o instanceof EapData)) { 153 return false; 154 } 155 EapData eapData = (EapData) o; 156 return eapType == eapData.eapType 157 && Arrays.equals(eapTypeData, eapData.eapTypeData); 158 } 159 160 /** 161 * Returns the hashCode value for this instance. 162 * 163 * @return the hashCode value for this instance 164 */ 165 @Override hashCode()166 public int hashCode() { 167 return Objects.hash(eapType, Arrays.hashCode(eapTypeData)); 168 } 169 170 /** 171 * Puts the byte-encoding for this EapData instance into the given ByteBuffer. 172 * 173 * @param b the ByteBuffer to write this EapData instance to 174 */ encodeToByteBuffer(ByteBuffer b)175 public void encodeToByteBuffer(ByteBuffer b) { 176 b.put((byte) eapType); 177 b.put(eapTypeData); 178 } 179 180 /** 181 * Returns whether the given eapType is a supported {@link EapType} value. 182 * 183 * @param eapType the value to be checked 184 * @return true iff the given eapType is a supported EAP Type 185 */ isSupportedEapType(int eapType)186 public static boolean isSupportedEapType(int eapType) { 187 return SUPPORTED_TYPES.contains(eapType); 188 } 189 validate()190 private void validate() { 191 if (this.eapTypeData == null) { 192 throw new IllegalArgumentException("EapTypeData byte[] must be non-null"); 193 } 194 if (!isSupportedEapType(this.eapType)) { 195 throw new IllegalArgumentException("eapType must be be a valid @EapType value"); 196 } 197 } 198 } 199