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.simaka; 18 19 import static com.android.internal.net.eap.EapAuthenticator.LOG; 20 21 import com.android.internal.annotations.VisibleForTesting; 22 import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAtPaddingException; 23 import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; 24 import com.android.internal.net.eap.exceptions.simaka.EapSimInvalidAtRandException; 25 26 import java.nio.BufferUnderflowException; 27 import java.nio.ByteBuffer; 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.HashMap; 31 import java.util.List; 32 import java.util.Map; 33 34 /** 35 * EapSimAkaAttribute represents a single EAP SIM/AKA Attribute. 36 * 37 * @see <a href="https://tools.ietf.org/html/rfc4186">RFC 4186, Extensible Authentication 38 * Protocol for Subscriber Identity Modules (EAP-SIM)</a> 39 * @see <a href="https://tools.ietf.org/html/rfc4187">RFC 4187, Extensible Authentication 40 * Protocol for Authentication and Key Agreement (EAP-AKA)</a> 41 * @see <a href="https://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml">EAP SIM/AKA 42 * Attributes</a> 43 */ 44 public abstract class EapSimAkaAttribute { 45 static final int LENGTH_SCALING = 4; 46 47 private static final int MIN_ATTR_LENGTH = 4; 48 49 public static final int SKIPPABLE_ATTRIBUTE_RANGE_START = 128; 50 51 // EAP non-Skippable Attribute values defined by IANA 52 // https://www.iana.org/assignments/eapsimaka-numbers/eapsimaka-numbers.xhtml 53 public static final int EAP_AT_RAND = 1; 54 public static final int EAP_AT_AUTN = 2; 55 public static final int EAP_AT_RES = 3; 56 public static final int EAP_AT_AUTS = 4; 57 public static final int EAP_AT_PADDING = 6; 58 public static final int EAP_AT_NONCE_MT = 7; 59 public static final int EAP_AT_PERMANENT_ID_REQ = 10; 60 public static final int EAP_AT_MAC = 11; 61 public static final int EAP_AT_NOTIFICATION = 12; 62 public static final int EAP_AT_ANY_ID_REQ = 13; 63 public static final int EAP_AT_IDENTITY = 14; 64 public static final int EAP_AT_VERSION_LIST = 15; 65 public static final int EAP_AT_SELECTED_VERSION = 16; 66 public static final int EAP_AT_FULLAUTH_ID_REQ = 17; 67 public static final int EAP_AT_COUNTER = 19; 68 public static final int EAP_AT_COUNTER_TOO_SMALL = 20; 69 public static final int EAP_AT_NONCE_S = 21; 70 public static final int EAP_AT_CLIENT_ERROR_CODE = 22; 71 public static final int EAP_AT_KDF_INPUT = 23; 72 public static final int EAP_AT_KDF = 24; 73 74 // EAP Skippable Attribute values defined by IANA 75 // https://www.iana.org/assignments/eapsimaka-numbers/eapsimaka-numbers.xhtml 76 public static final int EAP_AT_IV = 129; 77 public static final int EAP_AT_ENCR_DATA = 130; 78 public static final int EAP_AT_NEXT_PSEUDONYM = 132; 79 public static final int EAP_AT_NEXT_REAUTH_ID = 133; 80 public static final int EAP_AT_CHECKCODE = 134; 81 public static final int EAP_AT_RESULT_IND = 135; 82 public static final int EAP_AT_BIDDING = 136; 83 84 public static final Map<Integer, String> EAP_ATTRIBUTE_STRING = new HashMap<>(); 85 static { EAP_ATTRIBUTE_STRING.put(EAP_AT_RAND, "AT_RAND")86 EAP_ATTRIBUTE_STRING.put(EAP_AT_RAND, "AT_RAND"); EAP_ATTRIBUTE_STRING.put(EAP_AT_AUTN, "AT_AUTN")87 EAP_ATTRIBUTE_STRING.put(EAP_AT_AUTN, "AT_AUTN"); EAP_ATTRIBUTE_STRING.put(EAP_AT_RES, "AT_RES")88 EAP_ATTRIBUTE_STRING.put(EAP_AT_RES, "AT_RES"); EAP_ATTRIBUTE_STRING.put(EAP_AT_AUTS, "AT_AUTS")89 EAP_ATTRIBUTE_STRING.put(EAP_AT_AUTS, "AT_AUTS"); EAP_ATTRIBUTE_STRING.put(EAP_AT_PADDING, "AT_PADDING")90 EAP_ATTRIBUTE_STRING.put(EAP_AT_PADDING, "AT_PADDING"); EAP_ATTRIBUTE_STRING.put(EAP_AT_NONCE_MT, "AT_NONCE_MT")91 EAP_ATTRIBUTE_STRING.put(EAP_AT_NONCE_MT, "AT_NONCE_MT"); EAP_ATTRIBUTE_STRING.put(EAP_AT_PERMANENT_ID_REQ, "AT_PERMANENT_ID_REQ")92 EAP_ATTRIBUTE_STRING.put(EAP_AT_PERMANENT_ID_REQ, "AT_PERMANENT_ID_REQ"); EAP_ATTRIBUTE_STRING.put(EAP_AT_MAC, "AT_MAC")93 EAP_ATTRIBUTE_STRING.put(EAP_AT_MAC, "AT_MAC"); EAP_ATTRIBUTE_STRING.put(EAP_AT_NOTIFICATION, "AT_NOTIFICATION")94 EAP_ATTRIBUTE_STRING.put(EAP_AT_NOTIFICATION, "AT_NOTIFICATION"); EAP_ATTRIBUTE_STRING.put(EAP_AT_ANY_ID_REQ, "AT_ANY_ID_REQ")95 EAP_ATTRIBUTE_STRING.put(EAP_AT_ANY_ID_REQ, "AT_ANY_ID_REQ"); EAP_ATTRIBUTE_STRING.put(EAP_AT_IDENTITY, "AT_IDENTITY")96 EAP_ATTRIBUTE_STRING.put(EAP_AT_IDENTITY, "AT_IDENTITY"); EAP_ATTRIBUTE_STRING.put(EAP_AT_VERSION_LIST, "AT_VERSION_LIST")97 EAP_ATTRIBUTE_STRING.put(EAP_AT_VERSION_LIST, "AT_VERSION_LIST"); EAP_ATTRIBUTE_STRING.put(EAP_AT_SELECTED_VERSION, "AT_SELECTED_VERSION")98 EAP_ATTRIBUTE_STRING.put(EAP_AT_SELECTED_VERSION, "AT_SELECTED_VERSION"); EAP_ATTRIBUTE_STRING.put(EAP_AT_FULLAUTH_ID_REQ, "AT_FULLAUTH_ID_REQ")99 EAP_ATTRIBUTE_STRING.put(EAP_AT_FULLAUTH_ID_REQ, "AT_FULLAUTH_ID_REQ"); EAP_ATTRIBUTE_STRING.put(EAP_AT_COUNTER, "AT_COUNTER")100 EAP_ATTRIBUTE_STRING.put(EAP_AT_COUNTER, "AT_COUNTER"); EAP_ATTRIBUTE_STRING.put(EAP_AT_COUNTER_TOO_SMALL, "AT_COUNTER_TOO_SMALL")101 EAP_ATTRIBUTE_STRING.put(EAP_AT_COUNTER_TOO_SMALL, "AT_COUNTER_TOO_SMALL"); EAP_ATTRIBUTE_STRING.put(EAP_AT_NONCE_S, "AT_NONCE_S")102 EAP_ATTRIBUTE_STRING.put(EAP_AT_NONCE_S, "AT_NONCE_S"); EAP_ATTRIBUTE_STRING.put(EAP_AT_CLIENT_ERROR_CODE, "AT_CLIENT_ERROR_CODE")103 EAP_ATTRIBUTE_STRING.put(EAP_AT_CLIENT_ERROR_CODE, "AT_CLIENT_ERROR_CODE"); EAP_ATTRIBUTE_STRING.put(EAP_AT_KDF_INPUT, "AT_KDF_INPUT")104 EAP_ATTRIBUTE_STRING.put(EAP_AT_KDF_INPUT, "AT_KDF_INPUT"); EAP_ATTRIBUTE_STRING.put(EAP_AT_KDF, "AT_KDF")105 EAP_ATTRIBUTE_STRING.put(EAP_AT_KDF, "AT_KDF"); 106 EAP_ATTRIBUTE_STRING.put(EAP_AT_IV, "AT_IV")107 EAP_ATTRIBUTE_STRING.put(EAP_AT_IV, "AT_IV"); EAP_ATTRIBUTE_STRING.put(EAP_AT_ENCR_DATA, "AT_ENCR_DATA")108 EAP_ATTRIBUTE_STRING.put(EAP_AT_ENCR_DATA, "AT_ENCR_DATA"); EAP_ATTRIBUTE_STRING.put(EAP_AT_NEXT_PSEUDONYM, "AT_NEXT_PSEUDONYM")109 EAP_ATTRIBUTE_STRING.put(EAP_AT_NEXT_PSEUDONYM, "AT_NEXT_PSEUDONYM"); EAP_ATTRIBUTE_STRING.put(EAP_AT_NEXT_REAUTH_ID, "AT_NEXT_REAUTH_ID")110 EAP_ATTRIBUTE_STRING.put(EAP_AT_NEXT_REAUTH_ID, "AT_NEXT_REAUTH_ID"); EAP_ATTRIBUTE_STRING.put(EAP_AT_CHECKCODE, "AT_CHECKCODE")111 EAP_ATTRIBUTE_STRING.put(EAP_AT_CHECKCODE, "AT_CHECKCODE"); EAP_ATTRIBUTE_STRING.put(EAP_AT_RESULT_IND, "AT_RESULT_IND")112 EAP_ATTRIBUTE_STRING.put(EAP_AT_RESULT_IND, "AT_RESULT_IND"); EAP_ATTRIBUTE_STRING.put(EAP_AT_BIDDING, "AT_BIDDING")113 EAP_ATTRIBUTE_STRING.put(EAP_AT_BIDDING, "AT_BIDDING"); 114 } 115 116 public final int attributeType; 117 public final int lengthInBytes; 118 EapSimAkaAttribute(int attributeType, int lengthInBytes)119 protected EapSimAkaAttribute(int attributeType, int lengthInBytes) 120 throws EapSimAkaInvalidAttributeException { 121 this.attributeType = attributeType; 122 this.lengthInBytes = lengthInBytes; 123 124 if (lengthInBytes % LENGTH_SCALING != 0) { 125 throw new EapSimAkaInvalidAttributeException("Attribute length must be multiple of 4"); 126 } 127 } 128 129 /** 130 * Encodes this EapSimAkaAttribute into the given ByteBuffer 131 * 132 * @param byteBuffer the ByteBuffer that this instance will be written to 133 */ encode(ByteBuffer byteBuffer)134 public abstract void encode(ByteBuffer byteBuffer); 135 136 /** 137 * EapSimAkaReservedBytesAttribute represents any EAP-SIM/AKA attribute that is of the format: 138 * 139 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 140 * | Attribute Type (1B) | Length (1B) | Reserved (2B) | 141 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 142 * | Value... 143 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 144 * 145 * <p>Note: This Attribute type ignores (but preserves) the Reserved bytes. This is needed for 146 * calculating MACs in EAP-SIM/AKA. 147 */ 148 protected abstract static class EapSimAkaReservedBytesAttribute extends EapSimAkaAttribute { 149 protected static final int RESERVED_BYTES_LEN = 2; 150 151 @VisibleForTesting public final byte[] reservedBytes = new byte[RESERVED_BYTES_LEN]; 152 EapSimAkaReservedBytesAttribute( int attributeType, int lengthInBytes, ByteBuffer buffer)153 protected EapSimAkaReservedBytesAttribute( 154 int attributeType, int lengthInBytes, ByteBuffer buffer) 155 throws EapSimAkaInvalidAttributeException { 156 super(attributeType, lengthInBytes); 157 158 try { 159 buffer.get(reservedBytes); 160 } catch (BufferUnderflowException e) { 161 throw new EapSimAkaInvalidAttributeException("Invalid attribute length", e); 162 } 163 } 164 EapSimAkaReservedBytesAttribute(int attributeType, int lengthInBytes)165 protected EapSimAkaReservedBytesAttribute(int attributeType, int lengthInBytes) 166 throws EapSimAkaInvalidAttributeException { 167 super(attributeType, lengthInBytes); 168 } 169 EapSimAkaReservedBytesAttribute( int attributeType, int lengthInBytes, byte[] reservedBytes)170 protected EapSimAkaReservedBytesAttribute( 171 int attributeType, int lengthInBytes, byte[] reservedBytes) 172 throws EapSimAkaInvalidAttributeException { 173 this(attributeType, lengthInBytes); 174 175 if (reservedBytes.length != RESERVED_BYTES_LEN) { 176 throw new EapSimAkaInvalidAttributeException("Invalid attribute length"); 177 } 178 System.arraycopy( 179 reservedBytes, 180 0 /* srcPos */, 181 this.reservedBytes, 182 0 /* destPos */, 183 RESERVED_BYTES_LEN); 184 } 185 186 @Override encode(ByteBuffer buffer)187 public void encode(ByteBuffer buffer) { 188 encodeAttributeHeader(buffer); 189 190 buffer.put(reservedBytes); 191 } 192 } 193 encodeAttributeHeader(ByteBuffer byteBuffer)194 protected void encodeAttributeHeader(ByteBuffer byteBuffer) { 195 byteBuffer.put((byte) attributeType); 196 byteBuffer.put((byte) (lengthInBytes / LENGTH_SCALING)); 197 } 198 consumePadding(int bytesUsed, ByteBuffer byteBuffer)199 void consumePadding(int bytesUsed, ByteBuffer byteBuffer) { 200 int paddingRemaining = lengthInBytes - bytesUsed; 201 byteBuffer.get(new byte[paddingRemaining]); 202 } 203 addPadding(int bytesUsed, ByteBuffer byteBuffer)204 void addPadding(int bytesUsed, ByteBuffer byteBuffer) { 205 int paddingNeeded = lengthInBytes - bytesUsed; 206 byteBuffer.put(new byte[paddingNeeded]); 207 } 208 209 /** 210 * EapSimAkaUnsupportedAttribute represents any unsupported, skippable EAP-SIM attribute. 211 */ 212 public static class EapSimAkaUnsupportedAttribute extends EapSimAkaAttribute { 213 // Attribute Type (1B) + Attribute Length (1B) = 2B Header 214 private static final int HEADER_BYTES = 2; 215 216 public final byte[] data; 217 EapSimAkaUnsupportedAttribute( int attributeType, int lengthInBytes, ByteBuffer byteBuffer)218 public EapSimAkaUnsupportedAttribute( 219 int attributeType, 220 int lengthInBytes, 221 ByteBuffer byteBuffer) throws EapSimAkaInvalidAttributeException { 222 super(attributeType, lengthInBytes); 223 224 // Attribute not supported, but remaining attribute still needs to be saved 225 int remainingBytes = lengthInBytes - HEADER_BYTES; 226 data = new byte[remainingBytes]; 227 byteBuffer.get(data); 228 } 229 230 @VisibleForTesting EapSimAkaUnsupportedAttribute(int attributeType, int lengthInBytes, byte[] data)231 public EapSimAkaUnsupportedAttribute(int attributeType, int lengthInBytes, byte[] data) 232 throws EapSimAkaInvalidAttributeException { 233 super(attributeType, lengthInBytes); 234 this.data = data; 235 } 236 237 @Override encode(ByteBuffer byteBuffer)238 public void encode(ByteBuffer byteBuffer) { 239 encodeAttributeHeader(byteBuffer); 240 byteBuffer.put(data); 241 } 242 } 243 244 /** 245 * AtVersionList represents the AT_VERSION_LIST attribute defined in RFC 4186#10.2 246 */ 247 public static class AtVersionList extends EapSimAkaAttribute { 248 private static final int BYTES_PER_VERSION = 2; 249 250 public final List<Integer> versions = new ArrayList<>(); 251 AtVersionList(int lengthInBytes, ByteBuffer byteBuffer)252 public AtVersionList(int lengthInBytes, ByteBuffer byteBuffer) 253 throws EapSimAkaInvalidAttributeException { 254 super(EAP_AT_VERSION_LIST, lengthInBytes); 255 256 // number of bytes used to represent list (RFC 4186 Section 10.2) 257 int bytesInList = Short.toUnsignedInt(byteBuffer.getShort()); 258 if (bytesInList % BYTES_PER_VERSION != 0) { 259 throw new EapSimAkaInvalidAttributeException( 260 "Actual Version List Length must be multiple of 2"); 261 } 262 263 int numVersions = bytesInList / BYTES_PER_VERSION; 264 for (int i = 0; i < numVersions; i++) { 265 versions.add(Short.toUnsignedInt(byteBuffer.getShort())); 266 } 267 268 int bytesUsed = MIN_ATTR_LENGTH + (BYTES_PER_VERSION * versions.size()); 269 consumePadding(bytesUsed, byteBuffer); 270 } 271 272 @VisibleForTesting AtVersionList(int lengthInBytes, int... versions)273 public AtVersionList(int lengthInBytes, int... versions) 274 throws EapSimAkaInvalidAttributeException { 275 super(EAP_AT_VERSION_LIST, lengthInBytes); 276 for (int version : versions) { 277 this.versions.add(version); 278 } 279 } 280 281 @Override encode(ByteBuffer byteBuffer)282 public void encode(ByteBuffer byteBuffer) { 283 encodeAttributeHeader(byteBuffer); 284 285 byteBuffer.putShort((short) (versions.size() * BYTES_PER_VERSION)); 286 for (int i : versions) { 287 byteBuffer.putShort((short) i); 288 } 289 290 int bytesUsed = MIN_ATTR_LENGTH + (BYTES_PER_VERSION * versions.size()); 291 addPadding(bytesUsed, byteBuffer); 292 } 293 } 294 295 /** 296 * AtSelectedVersion represents the AT_SELECTED_VERSION attribute defined in RFC 4186#10.3 297 */ 298 public static class AtSelectedVersion extends EapSimAkaAttribute { 299 private static final String TAG = AtSelectedVersion.class.getSimpleName(); 300 private static final int LENGTH = LENGTH_SCALING; 301 302 public static final int SUPPORTED_VERSION = 1; 303 304 public final int selectedVersion; 305 AtSelectedVersion(int lengthInBytes, int selectedVersion)306 public AtSelectedVersion(int lengthInBytes, int selectedVersion) 307 throws EapSimAkaInvalidAttributeException { 308 super(EAP_AT_SELECTED_VERSION, LENGTH); 309 this.selectedVersion = selectedVersion; 310 311 if (lengthInBytes != LENGTH) { 312 throw new EapSimAkaInvalidAttributeException("Invalid Length specified"); 313 } 314 } 315 316 @VisibleForTesting AtSelectedVersion(int selectedVersion)317 public AtSelectedVersion(int selectedVersion) throws EapSimAkaInvalidAttributeException { 318 super(EAP_AT_SELECTED_VERSION, LENGTH); 319 this.selectedVersion = selectedVersion; 320 } 321 322 @Override encode(ByteBuffer byteBuffer)323 public void encode(ByteBuffer byteBuffer) { 324 encodeAttributeHeader(byteBuffer); 325 byteBuffer.putShort((short) selectedVersion); 326 } 327 328 /** 329 * Constructs and returns an AtSelectedVersion for the only supported version of EAP-SIM 330 * 331 * @return an AtSelectedVersion for the supported version (1) of EAP-SIM 332 */ getSelectedVersion()333 public static AtSelectedVersion getSelectedVersion() { 334 try { 335 return new AtSelectedVersion(LENGTH, SUPPORTED_VERSION); 336 } catch (EapSimAkaInvalidAttributeException ex) { 337 // this should never happen 338 LOG.wtf(TAG, 339 "Error thrown while creating AtSelectedVersion with correct length", ex); 340 throw new AssertionError("Impossible exception encountered", ex); 341 } 342 } 343 } 344 345 /** 346 * AtNonceMt represents the AT_NONCE_MT attribute defined in RFC 4186#10.4 347 */ 348 public static class AtNonceMt extends EapSimAkaReservedBytesAttribute { 349 private static final int LENGTH = 5 * LENGTH_SCALING; 350 351 public static final int NONCE_MT_LENGTH = 16; 352 353 public final byte[] nonceMt = new byte[NONCE_MT_LENGTH]; 354 AtNonceMt(int lengthInBytes, ByteBuffer byteBuffer)355 public AtNonceMt(int lengthInBytes, ByteBuffer byteBuffer) 356 throws EapSimAkaInvalidAttributeException { 357 super(EAP_AT_NONCE_MT, LENGTH, byteBuffer); 358 if (lengthInBytes != LENGTH) { 359 throw new EapSimAkaInvalidAttributeException("Invalid Length specified"); 360 } 361 362 byteBuffer.get(nonceMt); 363 } 364 365 @VisibleForTesting AtNonceMt(byte[] nonceMt)366 public AtNonceMt(byte[] nonceMt) throws EapSimAkaInvalidAttributeException { 367 super(EAP_AT_NONCE_MT, LENGTH); 368 369 if (nonceMt.length != NONCE_MT_LENGTH) { 370 throw new EapSimAkaInvalidAttributeException("NonceMt length must be 16B"); 371 } 372 System.arraycopy(nonceMt, 0, this.nonceMt, 0, NONCE_MT_LENGTH); 373 } 374 375 @Override encode(ByteBuffer byteBuffer)376 public void encode(ByteBuffer byteBuffer) { 377 super.encode(byteBuffer); 378 379 byteBuffer.put(nonceMt); 380 } 381 } 382 383 private abstract static class AtIdReq extends EapSimAkaReservedBytesAttribute { 384 private static final int ATTR_LENGTH = LENGTH_SCALING; 385 AtIdReq(int lengthInBytes, int attributeType, ByteBuffer byteBuffer)386 protected AtIdReq(int lengthInBytes, int attributeType, ByteBuffer byteBuffer) 387 throws EapSimAkaInvalidAttributeException { 388 super(attributeType, ATTR_LENGTH, byteBuffer); 389 390 if (lengthInBytes != ATTR_LENGTH) { 391 throw new EapSimAkaInvalidAttributeException("Invalid Length specified"); 392 } 393 } 394 395 @VisibleForTesting AtIdReq(int attributeType)396 protected AtIdReq(int attributeType) throws EapSimAkaInvalidAttributeException { 397 super(attributeType, ATTR_LENGTH); 398 } 399 } 400 401 /** 402 * AtPermanentIdReq represents the AT_PERMANENT_ID_REQ attribute defined in RFC 4186#10.5 and 403 * RFC 4187#10.2 404 */ 405 public static class AtPermanentIdReq extends AtIdReq { AtPermanentIdReq(int lengthInBytes, ByteBuffer byteBuffer)406 public AtPermanentIdReq(int lengthInBytes, ByteBuffer byteBuffer) 407 throws EapSimAkaInvalidAttributeException { 408 super(lengthInBytes, EAP_AT_PERMANENT_ID_REQ, byteBuffer); 409 } 410 411 @VisibleForTesting AtPermanentIdReq()412 public AtPermanentIdReq() throws EapSimAkaInvalidAttributeException { 413 super(EAP_AT_PERMANENT_ID_REQ); 414 } 415 } 416 417 /** 418 * AtAnyIdReq represents the AT_ANY_ID_REQ attribute defined in RFC 4186#10.6 and RFC 4187#10.3 419 */ 420 public static class AtAnyIdReq extends AtIdReq { AtAnyIdReq(int lengthInBytes, ByteBuffer byteBuffer)421 public AtAnyIdReq(int lengthInBytes, ByteBuffer byteBuffer) 422 throws EapSimAkaInvalidAttributeException { 423 super(lengthInBytes, EAP_AT_ANY_ID_REQ, byteBuffer); 424 } 425 426 @VisibleForTesting AtAnyIdReq()427 public AtAnyIdReq() throws EapSimAkaInvalidAttributeException { 428 super(EAP_AT_ANY_ID_REQ); 429 } 430 } 431 432 /** 433 * AtFullauthIdReq represents the AT_FULLAUTH_ID_REQ attribute defined in RFC 4186#10.7 and RFC 434 * 4187#10.4 435 */ 436 public static class AtFullauthIdReq extends AtIdReq { AtFullauthIdReq(int lengthInBytes, ByteBuffer byteBuffer)437 public AtFullauthIdReq(int lengthInBytes, ByteBuffer byteBuffer) 438 throws EapSimAkaInvalidAttributeException { 439 super(lengthInBytes, EAP_AT_FULLAUTH_ID_REQ, byteBuffer); 440 } 441 442 @VisibleForTesting AtFullauthIdReq()443 public AtFullauthIdReq() throws EapSimAkaInvalidAttributeException { 444 super(EAP_AT_FULLAUTH_ID_REQ); 445 } 446 } 447 448 /** 449 * AtIdentity represents the AT_IDENTITY attribute defined in RFC 4186#10.8 and RFC 4187#10.5 450 */ 451 public static class AtIdentity extends EapSimAkaAttribute { 452 public final byte[] identity; 453 AtIdentity(int lengthInBytes, ByteBuffer byteBuffer)454 public AtIdentity(int lengthInBytes, ByteBuffer byteBuffer) 455 throws EapSimAkaInvalidAttributeException { 456 super(EAP_AT_IDENTITY, lengthInBytes); 457 458 int identityLength = Short.toUnsignedInt(byteBuffer.getShort()); 459 identity = new byte[identityLength]; 460 byteBuffer.get(identity); 461 462 int bytesUsed = MIN_ATTR_LENGTH + identityLength; 463 consumePadding(bytesUsed, byteBuffer); 464 } 465 466 @VisibleForTesting AtIdentity(int lengthInBytes, byte[] identity)467 public AtIdentity(int lengthInBytes, byte[] identity) 468 throws EapSimAkaInvalidAttributeException { 469 super(EAP_AT_IDENTITY, lengthInBytes); 470 this.identity = identity; 471 } 472 473 @Override encode(ByteBuffer byteBuffer)474 public void encode(ByteBuffer byteBuffer) { 475 encodeAttributeHeader(byteBuffer); 476 byteBuffer.putShort((short) identity.length); 477 byteBuffer.put(identity); 478 479 int bytesUsed = MIN_ATTR_LENGTH + identity.length; 480 addPadding(bytesUsed, byteBuffer); 481 } 482 483 /** 484 * Creates and returns an AtIdentity instance for the given identity. 485 * 486 * @param identity byte-array representing the identity for the AtIdentity 487 * @return AtIdentity instance for the given identity byte-array 488 */ getAtIdentity(byte[] identity)489 public static AtIdentity getAtIdentity(byte[] identity) 490 throws EapSimAkaInvalidAttributeException { 491 int lengthInBytes = MIN_ATTR_LENGTH + identity.length; 492 if (lengthInBytes % LENGTH_SCALING != 0) { 493 lengthInBytes += LENGTH_SCALING - (lengthInBytes % LENGTH_SCALING); 494 } 495 496 return new AtIdentity(lengthInBytes, identity); 497 } 498 } 499 500 /** 501 * AtRandSim represents the AT_RAND attribute for EAP-SIM defined in RFC 4186#10.9 502 */ 503 public static class AtRandSim extends EapSimAkaReservedBytesAttribute { 504 private static final int RAND_LENGTH = 16; 505 private static final int MIN_RANDS = 2; 506 private static final int MAX_RANDS = 3; 507 508 public final List<byte[]> rands = new ArrayList<>(MAX_RANDS); 509 AtRandSim(int lengthInBytes, ByteBuffer byteBuffer)510 public AtRandSim(int lengthInBytes, ByteBuffer byteBuffer) 511 throws EapSimAkaInvalidAttributeException { 512 super(EAP_AT_RAND, lengthInBytes, byteBuffer); 513 514 int numRands = (lengthInBytes - MIN_ATTR_LENGTH) / RAND_LENGTH; 515 if (!isValidNumRands(numRands)) { 516 throw new EapSimInvalidAtRandException("Unexpected number of rands: " + numRands); 517 } 518 519 for (int i = 0; i < numRands; i++) { 520 byte[] rand = new byte[RAND_LENGTH]; 521 byteBuffer.get(rand); 522 523 // check for rand being unique (RFC 4186 Section 10.9) 524 for (int j = 0; j < i; j++) { 525 byte[] otherRand = rands.get(j); 526 if (Arrays.equals(rand, otherRand)) { 527 throw new EapSimAkaInvalidAttributeException("Received identical RANDs"); 528 } 529 } 530 rands.add(rand); 531 } 532 } 533 534 @VisibleForTesting AtRandSim(int lengthInBytes, byte[]... rands)535 public AtRandSim(int lengthInBytes, byte[]... rands) 536 throws EapSimAkaInvalidAttributeException { 537 super(EAP_AT_RAND, lengthInBytes); 538 539 if (!isValidNumRands(rands.length)) { 540 throw new EapSimInvalidAtRandException("Unexpected number of rands: " 541 + rands.length); 542 } 543 for (byte[] rand : rands) { 544 this.rands.add(rand); 545 } 546 } 547 isValidNumRands(int numRands)548 private boolean isValidNumRands(int numRands) { 549 // numRands is valid iff 2 <= numRands <= 3 550 return MIN_RANDS <= numRands && numRands <= MAX_RANDS; 551 } 552 553 @Override encode(ByteBuffer byteBuffer)554 public void encode(ByteBuffer byteBuffer) { 555 super.encode(byteBuffer); 556 557 for (byte[] rand : rands) { 558 byteBuffer.put(rand); 559 } 560 } 561 } 562 563 /** 564 * AtRandAka represents the AT_RAND attribute for EAP-AKA defined in RFC 4187#10.6 565 */ 566 public static class AtRandAka extends EapSimAkaReservedBytesAttribute { 567 private static final int ATTR_LENGTH = 5 * LENGTH_SCALING; 568 private static final int RAND_LENGTH = 16; 569 570 public final byte[] rand = new byte[RAND_LENGTH]; 571 AtRandAka(int lengthInBytes, ByteBuffer byteBuffer)572 public AtRandAka(int lengthInBytes, ByteBuffer byteBuffer) 573 throws EapSimAkaInvalidAttributeException { 574 super(EAP_AT_RAND, lengthInBytes, byteBuffer); 575 576 if (lengthInBytes != ATTR_LENGTH) { 577 throw new EapSimAkaInvalidAttributeException("Length must be 20B"); 578 } 579 580 byteBuffer.get(rand); 581 } 582 583 @VisibleForTesting AtRandAka(byte[] rand)584 public AtRandAka(byte[] rand) 585 throws EapSimAkaInvalidAttributeException { 586 super(EAP_AT_RAND, ATTR_LENGTH); 587 588 if (rand.length != RAND_LENGTH) { 589 throw new EapSimAkaInvalidAttributeException("Rand must be 16B"); 590 } 591 592 System.arraycopy(rand, 0, this.rand, 0, RAND_LENGTH); 593 } 594 595 @Override encode(ByteBuffer byteBuffer)596 public void encode(ByteBuffer byteBuffer) { 597 super.encode(byteBuffer); 598 599 byteBuffer.put(rand); 600 } 601 } 602 603 /** 604 * AtPadding represents the AT_PADDING attribute defined in RFC 4186#10.12 and RFC 4187#10.12 605 */ 606 public static class AtPadding extends EapSimAkaAttribute { 607 private static final int ATTR_HEADER = 2; 608 AtPadding(int lengthInBytes, ByteBuffer byteBuffer)609 public AtPadding(int lengthInBytes, ByteBuffer byteBuffer) 610 throws EapSimAkaInvalidAttributeException { 611 super(EAP_AT_PADDING, lengthInBytes); 612 613 int remainingBytes = lengthInBytes - ATTR_HEADER; 614 for (int i = 0; i < remainingBytes; i++) { 615 // Padding must be checked to all be 0x00 bytes (RFC 4186 Section 10.12) 616 if (byteBuffer.get() != 0) { 617 throw new EapSimAkaInvalidAtPaddingException("Padding bytes must all be 0x00"); 618 } 619 } 620 } 621 622 @VisibleForTesting AtPadding(int lengthInBytes)623 public AtPadding(int lengthInBytes) throws EapSimAkaInvalidAttributeException { 624 super(EAP_AT_PADDING, lengthInBytes); 625 } 626 627 @Override encode(ByteBuffer byteBuffer)628 public void encode(ByteBuffer byteBuffer) { 629 encodeAttributeHeader(byteBuffer); 630 631 addPadding(ATTR_HEADER, byteBuffer); 632 } 633 } 634 635 /** 636 * AtMac represents the AT_MAC attribute defined in RFC 4186#10.14 and RFC 4187#10.15 637 */ 638 public static class AtMac extends EapSimAkaReservedBytesAttribute { 639 private static final int ATTR_LENGTH = 5 * LENGTH_SCALING; 640 641 public static final int MAC_LENGTH = 4 * LENGTH_SCALING; 642 643 public final byte[] mac; 644 AtMac(int lengthInBytes, ByteBuffer byteBuffer)645 public AtMac(int lengthInBytes, ByteBuffer byteBuffer) 646 throws EapSimAkaInvalidAttributeException { 647 super(EAP_AT_MAC, lengthInBytes, byteBuffer); 648 649 if (lengthInBytes != ATTR_LENGTH) { 650 throw new EapSimAkaInvalidAttributeException("Invalid Length specified"); 651 } 652 653 mac = new byte[MAC_LENGTH]; 654 byteBuffer.get(mac); 655 } 656 657 // Constructs an AtMac with an empty MAC and empty RESERVED bytes. Should only be used for 658 // calculating MACs in outbound messages. AtMac()659 public AtMac() throws EapSimAkaInvalidAttributeException { 660 this(new byte[MAC_LENGTH]); 661 } 662 AtMac(byte[] mac)663 public AtMac(byte[] mac) throws EapSimAkaInvalidAttributeException { 664 this(new byte[RESERVED_BYTES_LEN], mac); 665 } 666 667 @VisibleForTesting AtMac(byte[] reservedBytes, byte[] mac)668 public AtMac(byte[] reservedBytes, byte[] mac) throws EapSimAkaInvalidAttributeException { 669 super(EAP_AT_MAC, ATTR_LENGTH, reservedBytes); 670 671 if (mac.length != MAC_LENGTH) { 672 throw new EapSimAkaInvalidAttributeException("Invalid length for MAC"); 673 } 674 this.mac = mac; 675 } 676 677 @Override encode(ByteBuffer byteBuffer)678 public void encode(ByteBuffer byteBuffer) { 679 super.encode(byteBuffer); 680 681 byteBuffer.put(mac); 682 } 683 684 /** 685 * Returns a copy of this AtMac with the MAC cleared (and the reserved bytes preserved). 686 * 687 * <p>Per RFC 4186 Section 10.14, the MAC should be calculated over the entire packet, with 688 * the value field of the MAC attribute set to zero. 689 */ getAtMacWithMacCleared()690 public AtMac getAtMacWithMacCleared() throws EapSimAkaInvalidAttributeException { 691 return new AtMac(reservedBytes, new byte[MAC_LENGTH]); 692 } 693 } 694 695 /** 696 * AtCounter represents the AT_COUNTER attribute defined in RFC 4186#10.15 and RFC 4187#10.16 697 */ 698 public static class AtCounter extends EapSimAkaAttribute { 699 private static final int ATTR_LENGTH = LENGTH_SCALING; 700 701 public final int counter; 702 AtCounter(int lengthInBytes, ByteBuffer byteBuffer)703 public AtCounter(int lengthInBytes, ByteBuffer byteBuffer) 704 throws EapSimAkaInvalidAttributeException { 705 super(EAP_AT_COUNTER, lengthInBytes); 706 707 if (lengthInBytes != ATTR_LENGTH) { 708 throw new EapSimAkaInvalidAttributeException("Invalid Length specified"); 709 } 710 711 this.counter = Short.toUnsignedInt(byteBuffer.getShort()); 712 } 713 714 @VisibleForTesting AtCounter(int counter)715 public AtCounter(int counter) throws EapSimAkaInvalidAttributeException { 716 super(EAP_AT_COUNTER, ATTR_LENGTH); 717 this.counter = counter; 718 } 719 720 @Override encode(ByteBuffer byteBuffer)721 public void encode(ByteBuffer byteBuffer) { 722 encodeAttributeHeader(byteBuffer); 723 byteBuffer.putShort((short) counter); 724 } 725 } 726 727 728 /** 729 * AtCounterTooSmall represents the AT_COUNTER_TOO_SMALL attribute defined in RFC 4186#10.16 and 730 * RFC 4187#10.17 731 */ 732 public static class AtCounterTooSmall extends EapSimAkaAttribute { 733 private static final int ATTR_LENGTH = LENGTH_SCALING; 734 private static final int ATTR_HEADER = 2; 735 AtCounterTooSmall(int lengthInBytes, ByteBuffer byteBuffer)736 public AtCounterTooSmall(int lengthInBytes, ByteBuffer byteBuffer) 737 throws EapSimAkaInvalidAttributeException { 738 super(EAP_AT_COUNTER_TOO_SMALL, lengthInBytes); 739 740 if (lengthInBytes != ATTR_LENGTH) { 741 throw new EapSimAkaInvalidAttributeException("Invalid Length specified"); 742 } 743 consumePadding(ATTR_HEADER, byteBuffer); 744 } 745 AtCounterTooSmall()746 public AtCounterTooSmall() throws EapSimAkaInvalidAttributeException { 747 super(EAP_AT_COUNTER_TOO_SMALL, ATTR_LENGTH); 748 } 749 750 @Override encode(ByteBuffer byteBuffer)751 public void encode(ByteBuffer byteBuffer) { 752 encodeAttributeHeader(byteBuffer); 753 addPadding(ATTR_HEADER, byteBuffer); 754 } 755 } 756 757 /** 758 * AtNonceS represents the AT_NONCE_S attribute defined in RFC 4186#10.17 and RFC 4187#10.18 759 * 760 * <p>This Nonce is generated by the server and used for fast re-authentication only. 761 */ 762 public static class AtNonceS extends EapSimAkaReservedBytesAttribute { 763 private static final int ATTR_LENGTH = 5 * LENGTH_SCALING; 764 private static final int NONCE_S_LENGTH = 4 * LENGTH_SCALING; 765 766 public final byte[] nonceS = new byte[NONCE_S_LENGTH]; 767 AtNonceS(int lengthInBytes, ByteBuffer byteBuffer)768 public AtNonceS(int lengthInBytes, ByteBuffer byteBuffer) 769 throws EapSimAkaInvalidAttributeException { 770 super(EAP_AT_NONCE_S, lengthInBytes, byteBuffer); 771 772 if (lengthInBytes != ATTR_LENGTH) { 773 throw new EapSimAkaInvalidAttributeException("Invalid Length specified"); 774 } 775 776 byteBuffer.get(nonceS); 777 } 778 779 @VisibleForTesting AtNonceS(byte[] nonceS)780 public AtNonceS(byte[] nonceS) throws EapSimAkaInvalidAttributeException { 781 super(EAP_AT_NONCE_S, ATTR_LENGTH); 782 783 if (nonceS.length != NONCE_S_LENGTH) { 784 throw new EapSimAkaInvalidAttributeException("NonceS length must be 16B"); 785 } 786 787 System.arraycopy(nonceS, 0, this.nonceS, 0, NONCE_S_LENGTH); 788 } 789 790 @Override encode(ByteBuffer byteBuffer)791 public void encode(ByteBuffer byteBuffer) { 792 super.encode(byteBuffer); 793 794 byteBuffer.put(nonceS); 795 } 796 } 797 798 /** 799 * AtNotification represents the AT_NOTIFICATION attribute defined in RFC 4186#10.18 and RFC 800 * 4187#10.19 801 */ 802 public static class AtNotification extends EapSimAkaAttribute { 803 private static final int ATTR_LENGTH = 4; 804 private static final int SUCCESS_MASK = 0x8000; 805 private static final int PRE_SUCCESSFUL_CHALLENGE_MASK = 0x4000; 806 807 // Notification codes defined in RFC 4186 Section 10.18 808 public static final int GENERAL_FAILURE_POST_CHALLENGE = 0; 809 public static final int GENERAL_FAILURE_PRE_CHALLENGE = 16384; // 0x4000 810 public static final int SUCCESS = 32768; // 0x8000 811 public static final int DENIED_ACCESS_POST_CHALLENGE = 1026; 812 public static final int USER_NOT_SUBSCRIBED_POST_CHALLENGE = 1031; 813 814 private static final Map<Integer, String> CODE_DEFS = loadCodeDefs(); 815 816 public final boolean isSuccessCode; 817 public final boolean isPreSuccessfulChallenge; 818 public final int notificationCode; 819 AtNotification(int lengthInBytes, ByteBuffer byteBuffer)820 public AtNotification(int lengthInBytes, ByteBuffer byteBuffer) 821 throws EapSimAkaInvalidAttributeException { 822 super(EAP_AT_NOTIFICATION, lengthInBytes); 823 824 if (lengthInBytes != ATTR_LENGTH) { 825 throw new EapSimAkaInvalidAttributeException("Invalid Length specified"); 826 } 827 828 notificationCode = Short.toUnsignedInt(byteBuffer.getShort()); 829 830 // If Success bit == 0, failure is implied 831 isSuccessCode = (notificationCode & SUCCESS_MASK) != 0; 832 833 // if Phase bit == 0, notification code can only be used after a successful 834 isPreSuccessfulChallenge = (notificationCode & PRE_SUCCESSFUL_CHALLENGE_MASK) != 0; 835 836 if (isSuccessCode && isPreSuccessfulChallenge) { 837 throw new EapSimAkaInvalidAttributeException("Invalid state specified"); 838 } 839 } 840 841 @VisibleForTesting AtNotification(int notificationCode)842 public AtNotification(int notificationCode) throws EapSimAkaInvalidAttributeException { 843 super(EAP_AT_NOTIFICATION, ATTR_LENGTH); 844 this.notificationCode = notificationCode; 845 846 // If Success bit == 0, failure is implied 847 isSuccessCode = (notificationCode & SUCCESS_MASK) != 0; 848 849 // if Phase bit == 0, notification code can only be used after a successful challenge 850 isPreSuccessfulChallenge = (notificationCode & PRE_SUCCESSFUL_CHALLENGE_MASK) != 0; 851 852 if (isSuccessCode && isPreSuccessfulChallenge) { 853 throw new EapSimAkaInvalidAttributeException("Invalid state specified"); 854 } 855 } 856 857 @Override encode(ByteBuffer byteBuffer)858 public void encode(ByteBuffer byteBuffer) { 859 encodeAttributeHeader(byteBuffer); 860 byteBuffer.putShort((short) notificationCode); 861 } 862 863 @Override toString()864 public String toString() { 865 String description = CODE_DEFS.getOrDefault(notificationCode, "Code not recognized"); 866 return "{Notification Code=" + notificationCode + ", descr=" + description + "}"; 867 } 868 loadCodeDefs()869 private static Map<Integer, String> loadCodeDefs() { 870 Map<Integer, String> defs = new HashMap<>(); 871 defs.put(GENERAL_FAILURE_POST_CHALLENGE, 872 "General failure after authentication. (Implies failure, used after successful" 873 + " authentication.)"); 874 defs.put(GENERAL_FAILURE_PRE_CHALLENGE, 875 "General failure. (Implies failure, used before authentication.)"); 876 defs.put(SUCCESS, 877 "Success. User has been successfully authenticated. (Does not imply failure," 878 + " used after successful authentication)."); 879 defs.put(DENIED_ACCESS_POST_CHALLENGE, 880 "User has been temporarily denied access to the requested service. (Implies" 881 + " failure, used after successful authentication.)"); 882 defs.put(USER_NOT_SUBSCRIBED_POST_CHALLENGE, 883 "User has not subscribed to the requested service. (Implies failure, used" 884 + " after successful authentication.)"); 885 return defs; 886 } 887 } 888 889 /** 890 * AtClientErrorCode represents the AT_CLIENT_ERROR_CODE attribute defined in RFC 4186#10.19 and 891 * RFC 4187#10.20 892 */ 893 public static class AtClientErrorCode extends EapSimAkaAttribute { 894 private static final String TAG = AtClientErrorCode.class.getSimpleName(); 895 private static final int ATTR_LENGTH = 4; 896 897 // Error codes defined in RFC 4186 Section 10.19 898 public static final AtClientErrorCode UNABLE_TO_PROCESS = getClientErrorCode(0); 899 public static final AtClientErrorCode UNSUPPORTED_VERSION = getClientErrorCode(1); 900 public static final AtClientErrorCode INSUFFICIENT_CHALLENGES = getClientErrorCode(2); 901 public static final AtClientErrorCode STALE_RANDS = getClientErrorCode(3); 902 903 public final int errorCode; 904 AtClientErrorCode(int lengthInBytes, int errorCode)905 public AtClientErrorCode(int lengthInBytes, int errorCode) 906 throws EapSimAkaInvalidAttributeException { 907 super(EAP_AT_CLIENT_ERROR_CODE, lengthInBytes); 908 909 if (lengthInBytes != ATTR_LENGTH) { 910 throw new EapSimAkaInvalidAttributeException("Invalid Length specified"); 911 } 912 913 this.errorCode = errorCode; 914 } 915 916 @Override encode(ByteBuffer byteBuffer)917 public void encode(ByteBuffer byteBuffer) { 918 encodeAttributeHeader(byteBuffer); 919 byteBuffer.putShort((short) errorCode); 920 } 921 getClientErrorCode(int errorCode)922 private static AtClientErrorCode getClientErrorCode(int errorCode) { 923 try { 924 return new AtClientErrorCode(ATTR_LENGTH, errorCode); 925 } catch (EapSimAkaInvalidAttributeException exception) { 926 LOG.wtf(TAG, "Exception thrown while making AtClientErrorCodeConstants"); 927 return null; 928 } 929 } 930 } 931 932 /** 933 * AtAutn represents the AT_AUTN attribute defined in RFC 4187#10.7 934 */ 935 public static class AtAutn extends EapSimAkaReservedBytesAttribute { 936 private static final int ATTR_LENGTH = 5 * LENGTH_SCALING; 937 private static final int AUTN_LENGTH = 16; 938 939 public final byte[] autn = new byte[AUTN_LENGTH]; 940 AtAutn(int lengthInBytes, ByteBuffer byteBuffer)941 public AtAutn(int lengthInBytes, ByteBuffer byteBuffer) 942 throws EapSimAkaInvalidAttributeException { 943 super(EAP_AT_AUTN, lengthInBytes, byteBuffer); 944 945 if (lengthInBytes != ATTR_LENGTH) { 946 throw new EapSimAkaInvalidAttributeException("Length must be 20B"); 947 } 948 949 byteBuffer.get(autn); 950 } 951 952 @VisibleForTesting AtAutn(byte[] autn)953 public AtAutn(byte[] autn) throws EapSimAkaInvalidAttributeException { 954 super(EAP_AT_AUTN, ATTR_LENGTH); 955 956 if (autn.length != AUTN_LENGTH) { 957 throw new EapSimAkaInvalidAttributeException("Autn must be 16B"); 958 } 959 960 System.arraycopy(autn, 0, this.autn, 0, AUTN_LENGTH); 961 } 962 963 @Override encode(ByteBuffer byteBuffer)964 public void encode(ByteBuffer byteBuffer) { 965 super.encode(byteBuffer); 966 967 byteBuffer.put(autn); 968 } 969 } 970 971 /** 972 * AtRes respresents the AT_RES attribute defined in RFC 4187#10.8 973 */ 974 public static class AtRes extends EapSimAkaAttribute { 975 private static final int BITS_PER_BYTE = 8; 976 private static final int MIN_RES_LEN_BYTES = 4; 977 private static final int MAX_RES_LEN_BYTES = 16; 978 979 public final byte[] res; 980 AtRes(int lengthInBytes, ByteBuffer byteBuffer)981 public AtRes(int lengthInBytes, ByteBuffer byteBuffer) 982 throws EapSimAkaInvalidAttributeException { 983 super(EAP_AT_RES, lengthInBytes); 984 985 // RES length is in bits (RFC 4187#10.8). 986 // RES length should be a multiple of 8 bits (TS 133 105#5.1.7.8) 987 int resLength = Short.toUnsignedInt(byteBuffer.getShort()); 988 if (resLength % BITS_PER_BYTE != 0) { 989 throw new EapSimAkaInvalidAttributeException("RES length must be multiple of 8"); 990 } 991 int resLengthBytes = resLength / BITS_PER_BYTE; 992 if (resLengthBytes < MIN_RES_LEN_BYTES || resLengthBytes > MAX_RES_LEN_BYTES) { 993 throw new EapSimAkaInvalidAttributeException( 994 "RES length must be: 4B <= len <= 16B"); 995 } 996 997 res = new byte[resLengthBytes]; 998 byteBuffer.get(res); 999 1000 int bytesUsed = MIN_ATTR_LENGTH + resLengthBytes; 1001 consumePadding(bytesUsed, byteBuffer); 1002 } 1003 1004 @VisibleForTesting AtRes(int lengthInBytes, byte[] res)1005 public AtRes(int lengthInBytes, byte[] res) throws EapSimAkaInvalidAttributeException { 1006 super(EAP_AT_RES, lengthInBytes); 1007 1008 if (res.length < MIN_RES_LEN_BYTES || res.length > MAX_RES_LEN_BYTES) { 1009 throw new EapSimAkaInvalidAttributeException( 1010 "RES length must be: 4B <= len <= 16B"); 1011 } 1012 1013 this.res = res; 1014 } 1015 1016 @Override encode(ByteBuffer byteBuffer)1017 public void encode(ByteBuffer byteBuffer) { 1018 encodeAttributeHeader(byteBuffer); 1019 1020 int resLenBits = res.length * BITS_PER_BYTE; 1021 byteBuffer.putShort((short) resLenBits); 1022 byteBuffer.put(res); 1023 1024 int bytesUsed = MIN_ATTR_LENGTH + res.length; 1025 addPadding(bytesUsed, byteBuffer); 1026 } 1027 1028 /** 1029 * Creates and returns an AtRes instance with the given res value. 1030 * 1031 * @param res byte-array RES value to be used for this 1032 * @return AtRes instance for the given RES value 1033 * @throws EapSimAkaInvalidAttributeException if the given res value has an invalid length 1034 */ getAtRes(byte[] res)1035 public static AtRes getAtRes(byte[] res) throws EapSimAkaInvalidAttributeException { 1036 // Attributes must be 4B-aligned, so there can be 0 to 3 padding bytes added 1037 int resLenBytes = MIN_ATTR_LENGTH + res.length; 1038 if (resLenBytes % LENGTH_SCALING != 0) { 1039 resLenBytes += LENGTH_SCALING - (resLenBytes % LENGTH_SCALING); 1040 } 1041 1042 return new AtRes(resLenBytes, res); 1043 } 1044 1045 /** 1046 * Checks whether the given RES length is valid. 1047 * 1048 * @param resLenBytes the RES length to be checked 1049 * @return true iff the given resLen is valid 1050 */ isValidResLen(int resLenBytes)1051 public static boolean isValidResLen(int resLenBytes) { 1052 return resLenBytes >= MIN_RES_LEN_BYTES && resLenBytes <= MAX_RES_LEN_BYTES; 1053 } 1054 } 1055 1056 /** 1057 * AtAuts represents the AT_AUTS attribute defined in RFC 4187#10.9 1058 */ 1059 public static class AtAuts extends EapSimAkaAttribute { 1060 private static final int ATTR_LENGTH = 4 * LENGTH_SCALING; 1061 public static final int AUTS_LENGTH = 14; 1062 1063 public final byte[] auts = new byte[AUTS_LENGTH]; 1064 AtAuts(int lengthInBytes, ByteBuffer byteBuffer)1065 public AtAuts(int lengthInBytes, ByteBuffer byteBuffer) 1066 throws EapSimAkaInvalidAttributeException { 1067 super(EAP_AT_AUTS, lengthInBytes); 1068 1069 if (lengthInBytes != ATTR_LENGTH) { 1070 throw new EapSimAkaInvalidAttributeException("Length must be 16B"); 1071 } 1072 1073 byteBuffer.get(auts); 1074 } 1075 AtAuts(byte[] auts)1076 public AtAuts(byte[] auts) throws EapSimAkaInvalidAttributeException { 1077 super(EAP_AT_AUTS, ATTR_LENGTH); 1078 1079 if (auts.length != AUTS_LENGTH) { 1080 throw new EapSimAkaInvalidAttributeException("Auts must be 14B"); 1081 } 1082 1083 System.arraycopy(auts, 0, this.auts, 0, AUTS_LENGTH); 1084 } 1085 1086 @Override encode(ByteBuffer byteBuffer)1087 public void encode(ByteBuffer byteBuffer) { 1088 encodeAttributeHeader(byteBuffer); 1089 1090 byteBuffer.put(auts); 1091 } 1092 } 1093 1094 /** 1095 * AtKdfInput represents the AT_KDF_INPUT attribute defined in RFC 5448#3.1 1096 */ 1097 public static class AtKdfInput extends EapSimAkaAttribute { 1098 public final byte[] networkName; 1099 AtKdfInput(int lengthInBytes, ByteBuffer byteBuffer)1100 public AtKdfInput(int lengthInBytes, ByteBuffer byteBuffer) 1101 throws EapSimAkaInvalidAttributeException { 1102 super(EAP_AT_KDF_INPUT, lengthInBytes); 1103 1104 int networkNameLength = Short.toUnsignedInt(byteBuffer.getShort()); 1105 networkName = new byte[networkNameLength]; 1106 byteBuffer.get(networkName); 1107 1108 int bytesUsed = MIN_ATTR_LENGTH + networkNameLength; 1109 consumePadding(bytesUsed, byteBuffer); 1110 } 1111 1112 @VisibleForTesting AtKdfInput(int lengthInbytes, byte[] networkName)1113 public AtKdfInput(int lengthInbytes, byte[] networkName) 1114 throws EapSimAkaInvalidAttributeException { 1115 super(EAP_AT_KDF_INPUT, lengthInbytes); 1116 1117 this.networkName = networkName; 1118 } 1119 1120 @Override encode(ByteBuffer byteBuffer)1121 public void encode(ByteBuffer byteBuffer) { 1122 encodeAttributeHeader(byteBuffer); 1123 byteBuffer.putShort((short) networkName.length); 1124 byteBuffer.put(networkName); 1125 1126 int bytesUsed = MIN_ATTR_LENGTH + networkName.length; 1127 addPadding(bytesUsed, byteBuffer); 1128 } 1129 } 1130 1131 /** 1132 * AdKdf represents the AT_KDF attribute defined in RFC 5448#3.2 1133 */ 1134 public static class AtKdf extends EapSimAkaAttribute { 1135 private static final int ATTR_LENGTH = MIN_ATTR_LENGTH; 1136 1137 public final int kdf; 1138 AtKdf(int lengthInBytes, ByteBuffer buffer)1139 public AtKdf(int lengthInBytes, ByteBuffer buffer) 1140 throws EapSimAkaInvalidAttributeException { 1141 super(EAP_AT_KDF, lengthInBytes); 1142 1143 if (lengthInBytes != ATTR_LENGTH) { 1144 throw new EapSimAkaInvalidAttributeException("AtKdf length must be 4B"); 1145 } 1146 1147 kdf = Short.toUnsignedInt(buffer.getShort()); 1148 } 1149 1150 @VisibleForTesting AtKdf(int kdf)1151 public AtKdf(int kdf) throws EapSimAkaInvalidAttributeException { 1152 super(EAP_AT_KDF, ATTR_LENGTH); 1153 1154 this.kdf = kdf; 1155 } 1156 1157 @Override encode(ByteBuffer byteBuffer)1158 public void encode(ByteBuffer byteBuffer) { 1159 encodeAttributeHeader(byteBuffer); 1160 1161 byteBuffer.putShort((short) kdf); 1162 } 1163 } 1164 1165 /** 1166 * AtBidding represents the AT_BIDDING attribute defined in RFC 5448#4 1167 */ 1168 public static class AtBidding extends EapSimAkaAttribute { 1169 private static final int ATTR_LENGTH = MIN_ATTR_LENGTH; 1170 private static final int SUPPORTS_EAP_AKA_PRIME_MASK = 0x8000; 1171 1172 public final boolean doesServerSupportEapAkaPrime; 1173 AtBidding(int lengthInBytes, ByteBuffer buffer)1174 public AtBidding(int lengthInBytes, ByteBuffer buffer) 1175 throws EapSimAkaInvalidAttributeException { 1176 super(EAP_AT_BIDDING, lengthInBytes); 1177 1178 if (lengthInBytes != ATTR_LENGTH) { 1179 throw new EapSimAkaInvalidAttributeException("AtBidding length must be 4B"); 1180 } 1181 1182 int serverFlag = Short.toUnsignedInt(buffer.getShort()); 1183 doesServerSupportEapAkaPrime = (serverFlag & SUPPORTS_EAP_AKA_PRIME_MASK) != 0; 1184 } 1185 1186 @VisibleForTesting AtBidding(boolean doesServerSupportEapAkaPrime)1187 public AtBidding(boolean doesServerSupportEapAkaPrime) 1188 throws EapSimAkaInvalidAttributeException { 1189 super(EAP_AT_BIDDING, ATTR_LENGTH); 1190 1191 this.doesServerSupportEapAkaPrime = doesServerSupportEapAkaPrime; 1192 } 1193 1194 @Override encode(ByteBuffer byteBuffer)1195 public void encode(ByteBuffer byteBuffer) { 1196 encodeAttributeHeader(byteBuffer); 1197 1198 int flagToWrite = doesServerSupportEapAkaPrime ? SUPPORTS_EAP_AKA_PRIME_MASK : 0; 1199 byteBuffer.putShort((short) flagToWrite); 1200 } 1201 } 1202 } 1203