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