| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.ike.eap.message; |
| |
| import android.util.Log; |
| |
| import com.android.ike.eap.exceptions.EapSimInvalidAtPaddingException; |
| import com.android.ike.eap.exceptions.EapSimInvalidAtRandException; |
| import com.android.ike.eap.exceptions.EapSimInvalidAttributeException; |
| import com.android.internal.annotations.VisibleForTesting; |
| |
| import java.nio.ByteBuffer; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * EapSimAttribute represents a single EAP-SIM Attribute. |
| * |
| * @see <a href="https://tools.ietf.org/html/rfc4186#section-10">RFC 4186, EAP-SIM Authentication, |
| * Section 10</a> |
| */ |
| public abstract class EapSimAttribute { |
| static final int LENGTH_SCALING = 4; |
| |
| private static final int MIN_ATTR_LENGTH = 4; |
| |
| public static final int SKIPPABLE_ATTRIBUTE_RANGE_START = 128; |
| |
| // EAP non-Skippable Attribute values defined by IANA |
| // https://www.iana.org/assignments/eapsimaka-numbers/eapsimaka-numbers.xhtml |
| public static final int EAP_AT_RAND = 1; |
| public static final int EAP_AT_PADDING = 6; |
| public static final int EAP_AT_NONCE_MT = 7; |
| public static final int EAP_AT_PERMANENT_ID_REQ = 10; |
| public static final int EAP_AT_MAC = 11; |
| public static final int EAP_AT_NOTIFICATION = 12; |
| public static final int EAP_AT_ANY_ID_REQ = 13; |
| public static final int EAP_AT_IDENTITY = 14; |
| public static final int EAP_AT_VERSION_LIST = 15; |
| public static final int EAP_AT_SELECTED_VERSION = 16; |
| public static final int EAP_AT_FULLAUTH_ID_REQ = 17; |
| public static final int EAP_AT_COUNTER = 19; |
| public static final int EAP_AT_COUNTER_TOO_SMALL = 20; |
| public static final int EAP_AT_NONCE_S = 21; |
| public static final int EAP_AT_CLIENT_ERROR_CODE = 22; |
| |
| // EAP Skippable Attribute values defined by IANA |
| // https://www.iana.org/assignments/eapsimaka-numbers/eapsimaka-numbers.xhtml |
| public static final int EAP_AT_IV = 129; |
| public static final int EAP_AT_ENCR_DATA = 130; |
| public static final int EAP_AT_NEXT_PSEUDONYM = 132; |
| public static final int EAP_AT_NEXT_REAUTH_ID = 133; |
| public static final int EAP_AT_RESULT_IND = 135; |
| |
| public final int attributeType; |
| public final int lengthInBytes; |
| |
| protected EapSimAttribute(int attributeType, int lengthInBytes) |
| throws EapSimInvalidAttributeException { |
| this.attributeType = attributeType; |
| this.lengthInBytes = lengthInBytes; |
| |
| if (lengthInBytes % LENGTH_SCALING != 0) { |
| throw new EapSimInvalidAttributeException("Attribute length must be multiple of 4"); |
| } |
| } |
| |
| /** |
| * Encodes this EapSimAttribute into the given ByteBuffer |
| * |
| * @param byteBuffer the ByteBuffer that this instance will be written to |
| */ |
| public abstract void encode(ByteBuffer byteBuffer); |
| |
| protected void encodeAttributeHeader(ByteBuffer byteBuffer) { |
| byteBuffer.put((byte) attributeType); |
| byteBuffer.put((byte) (lengthInBytes / LENGTH_SCALING)); |
| } |
| |
| void consumePadding(int bytesUsed, ByteBuffer byteBuffer) { |
| int paddingRemaining = lengthInBytes - bytesUsed; |
| byteBuffer.get(new byte[paddingRemaining]); |
| } |
| |
| void addPadding(int bytesUsed, ByteBuffer byteBuffer) { |
| int paddingNeeded = lengthInBytes - bytesUsed; |
| byteBuffer.put(new byte[paddingNeeded]); |
| } |
| |
| /** |
| * EapSimUnsupportedAttribute represents any unsupported, skippable EAP-SIM attribute. |
| */ |
| public static class EapSimUnsupportedAttribute extends EapSimAttribute { |
| // Attribute Type (1B) + Attribute Length (1B) = 2B Header |
| private static final int HEADER_BYTES = 2; |
| |
| public final byte[] data; |
| |
| public EapSimUnsupportedAttribute(int attributeType, int lengthInBytes, |
| ByteBuffer byteBuffer) throws EapSimInvalidAttributeException { |
| super(attributeType, lengthInBytes); |
| |
| // Attribute not supported, but remaining attribute still needs to be saved |
| int remainingBytes = lengthInBytes - HEADER_BYTES; |
| data = new byte[remainingBytes]; |
| byteBuffer.get(data); |
| } |
| |
| @VisibleForTesting |
| public EapSimUnsupportedAttribute(int attributeType, int lengthInBytes, byte[] data) |
| throws EapSimInvalidAttributeException { |
| super(attributeType, lengthInBytes); |
| this.data = data; |
| } |
| |
| @Override |
| public void encode(ByteBuffer byteBuffer) { |
| encodeAttributeHeader(byteBuffer); |
| byteBuffer.put(data); |
| } |
| } |
| |
| /** |
| * AtVersionList represents the AT_VERSION_LIST attribute defined in RFC 4186 Section 10.2 |
| */ |
| public static class AtVersionList extends EapSimAttribute { |
| private static final int BYTES_PER_VERSION = 2; |
| |
| public final List<Integer> versions = new ArrayList<>(); |
| |
| public AtVersionList(int lengthInBytes, ByteBuffer byteBuffer) |
| throws EapSimInvalidAttributeException { |
| super(EAP_AT_VERSION_LIST, lengthInBytes); |
| |
| // number of bytes used to represent list (RFC 4186 Section 10.2) |
| int bytesInList = Short.toUnsignedInt(byteBuffer.getShort()); |
| if (bytesInList % BYTES_PER_VERSION != 0) { |
| throw new EapSimInvalidAttributeException( |
| "Actual Version List Length must be multiple of 2"); |
| } |
| |
| int numVersions = bytesInList / BYTES_PER_VERSION; |
| for (int i = 0; i < numVersions; i++) { |
| versions.add(Short.toUnsignedInt(byteBuffer.getShort())); |
| } |
| |
| int bytesUsed = MIN_ATTR_LENGTH + (BYTES_PER_VERSION * versions.size()); |
| consumePadding(bytesUsed, byteBuffer); |
| } |
| |
| @VisibleForTesting |
| public AtVersionList(int lengthInBytes, int... versions) |
| throws EapSimInvalidAttributeException { |
| super(EAP_AT_VERSION_LIST, lengthInBytes); |
| for (int version : versions) { |
| this.versions.add(version); |
| } |
| } |
| |
| @Override |
| public void encode(ByteBuffer byteBuffer) { |
| encodeAttributeHeader(byteBuffer); |
| |
| byteBuffer.putShort((short) (versions.size() * BYTES_PER_VERSION)); |
| for (int i : versions) { |
| byteBuffer.putShort((short) i); |
| } |
| |
| int bytesUsed = MIN_ATTR_LENGTH + (BYTES_PER_VERSION * versions.size()); |
| addPadding(bytesUsed, byteBuffer); |
| } |
| } |
| |
| /** |
| * AtSelectedVersion represents the AT_SELECTED_VERSION attribute defined in RFC 4186 Section |
| * 10.3 |
| */ |
| public static class AtSelectedVersion extends EapSimAttribute { |
| private static final String TAG = AtSelectedVersion.class.getSimpleName(); |
| private static final int LENGTH = LENGTH_SCALING; |
| |
| public static final int SUPPORTED_VERSION = 1; |
| |
| public final int selectedVersion; |
| |
| public AtSelectedVersion(int lengthInBytes, int selectedVersion) |
| throws EapSimInvalidAttributeException { |
| super(EAP_AT_SELECTED_VERSION, LENGTH); |
| this.selectedVersion = selectedVersion; |
| |
| if (lengthInBytes != LENGTH) { |
| throw new EapSimInvalidAttributeException("Invalid Length specified"); |
| } |
| } |
| |
| @VisibleForTesting |
| public AtSelectedVersion(int selectedVersion) throws EapSimInvalidAttributeException { |
| super(EAP_AT_SELECTED_VERSION, LENGTH); |
| this.selectedVersion = selectedVersion; |
| } |
| |
| @Override |
| public void encode(ByteBuffer byteBuffer) { |
| encodeAttributeHeader(byteBuffer); |
| byteBuffer.putShort((short) selectedVersion); |
| } |
| |
| /** |
| * Constructs and returns an AtSelectedVersion for the only supported version of EAP-SIM |
| * |
| * @return an AtSelectedVersion for the supported version (1) of EAP-SIM |
| */ |
| public static AtSelectedVersion getSelectedVersion() { |
| try { |
| return new AtSelectedVersion(LENGTH, SUPPORTED_VERSION); |
| } catch (EapSimInvalidAttributeException ex) { |
| Log.wtf(TAG, |
| "Error thrown while creating AtSelectedVersion with correct length", ex); |
| throw new AssertionError("Impossible exception encountered", ex); |
| } |
| } |
| } |
| |
| /** |
| * AtNonceMt represents the AT_NONCE_MT attribute defined in RFC 4186 Section 10.4 |
| */ |
| public static class AtNonceMt extends EapSimAttribute { |
| private static final int LENGTH = 5 * LENGTH_SCALING; |
| private static final int RESERVED_BYTES = 2; |
| |
| public static final int NONCE_MT_LENGTH = 16; |
| |
| public final byte[] nonceMt = new byte[NONCE_MT_LENGTH]; |
| |
| public AtNonceMt(int lengthInBytes, ByteBuffer byteBuffer) |
| throws EapSimInvalidAttributeException { |
| super(EAP_AT_NONCE_MT, LENGTH); |
| if (lengthInBytes != LENGTH) { |
| throw new EapSimInvalidAttributeException("Invalid Length specified"); |
| } |
| |
| // next two bytes are reserved (RFC 4186 Section 10.4) |
| byteBuffer.get(new byte[RESERVED_BYTES]); |
| byteBuffer.get(nonceMt); |
| } |
| |
| @VisibleForTesting |
| public AtNonceMt(byte[] nonceMt) throws EapSimInvalidAttributeException { |
| super(EAP_AT_NONCE_MT, LENGTH); |
| for (int i = 0; i < nonceMt.length; i++) { |
| this.nonceMt[i] = nonceMt[i]; |
| } |
| } |
| |
| @Override |
| public void encode(ByteBuffer byteBuffer) { |
| encodeAttributeHeader(byteBuffer); |
| byteBuffer.put(new byte[RESERVED_BYTES]); |
| byteBuffer.put(nonceMt); |
| } |
| } |
| |
| private abstract static class AtIdReq extends EapSimAttribute { |
| private static final int ATTR_LENGTH = LENGTH_SCALING; |
| private static final int RESERVED_BYTES = 2; |
| |
| protected AtIdReq(int lengthInBytes, int attributeType, ByteBuffer byteBuffer) |
| throws EapSimInvalidAttributeException { |
| super(attributeType, ATTR_LENGTH); |
| |
| if (lengthInBytes != ATTR_LENGTH) { |
| throw new EapSimInvalidAttributeException("Invalid Length specified"); |
| } |
| |
| // next two bytes are reserved (RFC 4186 Section 10.5-10.7) |
| byteBuffer.get(new byte[RESERVED_BYTES]); |
| } |
| |
| @VisibleForTesting |
| protected AtIdReq(int attributeType) throws EapSimInvalidAttributeException { |
| super(attributeType, ATTR_LENGTH); |
| } |
| |
| @Override |
| public void encode(ByteBuffer byteBuffer) { |
| encodeAttributeHeader(byteBuffer); |
| byteBuffer.put(new byte[RESERVED_BYTES]); |
| } |
| } |
| |
| /** |
| * AtPermanentIdReq represents the AT_PERMANENT_ID_REQ attribute defined in RFC 4186 Section |
| * 10.5 |
| */ |
| public static class AtPermanentIdReq extends AtIdReq { |
| public AtPermanentIdReq(int lengthInBytes, ByteBuffer byteBuffer) |
| throws EapSimInvalidAttributeException { |
| super(lengthInBytes, EAP_AT_PERMANENT_ID_REQ, byteBuffer); |
| } |
| |
| @VisibleForTesting |
| public AtPermanentIdReq() throws EapSimInvalidAttributeException { |
| super(EAP_AT_PERMANENT_ID_REQ); |
| } |
| } |
| |
| /** |
| * AtAnyIdReq represents the AT_ANY_ID_REQ attribute defined in RFC 4186 Section 10.6 |
| */ |
| public static class AtAnyIdReq extends AtIdReq { |
| public AtAnyIdReq(int lengthInBytes, ByteBuffer byteBuffer) |
| throws EapSimInvalidAttributeException { |
| super(lengthInBytes, EAP_AT_ANY_ID_REQ, byteBuffer); |
| } |
| |
| @VisibleForTesting |
| public AtAnyIdReq() throws EapSimInvalidAttributeException { |
| super(EAP_AT_ANY_ID_REQ); |
| } |
| } |
| |
| /** |
| * AtFullauthIdReq represents the AT_FULLAUTH_ID_REQ attribute defined in RFC 4186 Section 10.7 |
| */ |
| public static class AtFullauthIdReq extends AtIdReq { |
| public AtFullauthIdReq(int lengthInBytes, ByteBuffer byteBuffer) |
| throws EapSimInvalidAttributeException { |
| super(lengthInBytes, EAP_AT_FULLAUTH_ID_REQ, byteBuffer); |
| } |
| |
| @VisibleForTesting |
| public AtFullauthIdReq() throws EapSimInvalidAttributeException { |
| super(EAP_AT_FULLAUTH_ID_REQ); |
| } |
| } |
| |
| /** |
| * AtIdentity represents the AT_IDENTITY attribute defined in RFC 4186 Section 10.8 |
| */ |
| public static class AtIdentity extends EapSimAttribute { |
| public final byte[] identity; |
| |
| public AtIdentity(int lengthInBytes, ByteBuffer byteBuffer) |
| throws EapSimInvalidAttributeException { |
| super(EAP_AT_IDENTITY, lengthInBytes); |
| |
| int identityLength = Short.toUnsignedInt(byteBuffer.getShort()); |
| identity = new byte[identityLength]; |
| byteBuffer.get(identity); |
| |
| int bytesUsed = MIN_ATTR_LENGTH + identityLength; |
| consumePadding(bytesUsed, byteBuffer); |
| } |
| |
| @VisibleForTesting |
| public AtIdentity(int lengthInBytes, byte[] identity) |
| throws EapSimInvalidAttributeException { |
| super(EAP_AT_IDENTITY, lengthInBytes); |
| this.identity = identity; |
| } |
| |
| @Override |
| public void encode(ByteBuffer byteBuffer) { |
| encodeAttributeHeader(byteBuffer); |
| byteBuffer.putShort((short) identity.length); |
| byteBuffer.put(identity); |
| |
| int bytesUsed = MIN_ATTR_LENGTH + identity.length; |
| addPadding(bytesUsed, byteBuffer); |
| } |
| |
| /** |
| * Creates and returns an AtIdentity instance for the given identity. |
| * |
| * @param identity byte-array representing the identity for the AtIdentity |
| * @return AtIdentity instance for the given identity byte-array |
| */ |
| public static AtIdentity getAtIdentity(byte[] identity) |
| throws EapSimInvalidAttributeException { |
| int lengthInBytes = MIN_ATTR_LENGTH + identity.length; |
| if (lengthInBytes % LENGTH_SCALING != 0) { |
| lengthInBytes += LENGTH_SCALING - (lengthInBytes % LENGTH_SCALING); |
| } |
| |
| return new AtIdentity(lengthInBytes, identity); |
| } |
| } |
| |
| /** |
| * AtRand represents the AT_RAND attribute defined in RFC 4186 Section 10.9 |
| */ |
| public static class AtRand extends EapSimAttribute { |
| private static final int RAND_LENGTH = 16; |
| private static final int RESERVED_BYTES = 2; |
| private static final int MIN_RANDS = 2; |
| private static final int MAX_RANDS = 3; |
| |
| public final List<byte[]> rands = new ArrayList<>(MAX_RANDS); |
| |
| public AtRand(int lengthInBytes, ByteBuffer byteBuffer) |
| throws EapSimInvalidAttributeException { |
| super(EAP_AT_RAND, lengthInBytes); |
| |
| // next two bytes are reserved (RFC 4186 Section 10.9) |
| byteBuffer.get(new byte[RESERVED_BYTES]); |
| |
| int numRands = (lengthInBytes - MIN_ATTR_LENGTH) / RAND_LENGTH; |
| if (!isValidNumRands(numRands)) { |
| throw new EapSimInvalidAtRandException("Unexpected number of rands: " + numRands); |
| } |
| |
| for (int i = 0; i < numRands; i++) { |
| byte[] rand = new byte[RAND_LENGTH]; |
| byteBuffer.get(rand); |
| |
| // check for rand being unique (RFC 4186 Section 10.9) |
| for (int j = 0; j < i; j++) { |
| byte[] otherRand = rands.get(j); |
| if (Arrays.equals(rand, otherRand)) { |
| throw new EapSimInvalidAttributeException("Received two identical RANDs"); |
| } |
| } |
| rands.add(rand); |
| } |
| } |
| |
| @VisibleForTesting |
| public AtRand(int lengthInBytes, byte[]... rands) throws EapSimInvalidAttributeException { |
| super(EAP_AT_RAND, lengthInBytes); |
| |
| if (!isValidNumRands(rands.length)) { |
| throw new EapSimInvalidAtRandException("Unexpected number of rands: " |
| + rands.length); |
| } |
| for (byte[] rand : rands) { |
| this.rands.add(rand); |
| } |
| } |
| |
| private boolean isValidNumRands(int numRands) { |
| // numRands is valid iff 2 <= numRands <= 3 |
| return MIN_RANDS <= numRands && numRands <= MAX_RANDS; |
| } |
| |
| @Override |
| public void encode(ByteBuffer byteBuffer) { |
| encodeAttributeHeader(byteBuffer); |
| byteBuffer.put(new byte[RESERVED_BYTES]); |
| |
| for (byte[] rand : rands) { |
| byteBuffer.put(rand); |
| } |
| } |
| } |
| |
| /** |
| * AtPadding represents the AT_PADDING attribute defined in RFC 4186 Section 10.12 |
| */ |
| public static class AtPadding extends EapSimAttribute { |
| private static final int ATTR_HEADER = 2; |
| |
| public AtPadding(int lengthInBytes, ByteBuffer byteBuffer) |
| throws EapSimInvalidAttributeException { |
| super(EAP_AT_PADDING, lengthInBytes); |
| |
| int remainingBytes = lengthInBytes - ATTR_HEADER; |
| for (int i = 0; i < remainingBytes; i++) { |
| // Padding must be checked to all be 0x00 bytes (RFC 4186 Section 10.12) |
| if (byteBuffer.get() != 0) { |
| throw new EapSimInvalidAtPaddingException("Padding bytes must all be 0x00"); |
| } |
| } |
| } |
| |
| @VisibleForTesting |
| public AtPadding(int lengthInBytes) throws EapSimInvalidAttributeException { |
| super(EAP_AT_PADDING, lengthInBytes); |
| } |
| |
| @Override |
| public void encode(ByteBuffer byteBuffer) { |
| encodeAttributeHeader(byteBuffer); |
| |
| addPadding(ATTR_HEADER, byteBuffer); |
| } |
| } |
| |
| /** |
| * AtMac represents the AT_MAC attribute defined in RFC 4186 Section 10.14 |
| */ |
| public static class AtMac extends EapSimAttribute { |
| private static final int ATTR_LENGTH = 5 * LENGTH_SCALING; |
| private static final int RESERVED_BYTES = 2; |
| |
| public static final int MAC_LENGTH = 4 * LENGTH_SCALING; |
| |
| public final byte[] mac; |
| |
| public AtMac(int lengthInBytes, ByteBuffer byteBuffer) |
| throws EapSimInvalidAttributeException { |
| super(EAP_AT_MAC, lengthInBytes); |
| |
| if (lengthInBytes != ATTR_LENGTH) { |
| throw new EapSimInvalidAttributeException("Invalid Length specified"); |
| } |
| |
| // next two bytes are reserved (RFC 4186 Section 10.14) |
| byteBuffer.get(new byte[RESERVED_BYTES]); |
| |
| mac = new byte[MAC_LENGTH]; |
| byteBuffer.get(mac); |
| } |
| |
| // Used for calculating MACs. Per RFC 4186 Section 10.14, the MAC should be calculated over |
| // the entire packet, with the value field of the MAC attribute set to zero. |
| public AtMac() throws EapSimInvalidAttributeException { |
| super(EAP_AT_MAC, ATTR_LENGTH); |
| mac = new byte[MAC_LENGTH]; |
| } |
| |
| public AtMac(byte[] mac) throws EapSimInvalidAttributeException { |
| super(EAP_AT_MAC, ATTR_LENGTH); |
| this.mac = mac; |
| |
| if (mac.length != MAC_LENGTH) { |
| throw new EapSimInvalidAttributeException("Invalid length for MAC"); |
| } |
| } |
| |
| @Override |
| public void encode(ByteBuffer byteBuffer) { |
| encodeAttributeHeader(byteBuffer); |
| byteBuffer.put(new byte[RESERVED_BYTES]); |
| byteBuffer.put(mac); |
| } |
| } |
| |
| /** |
| * AtCounter represents the AT_COUNTER attribute defined in RFC 4186 Section 10.15 |
| */ |
| public static class AtCounter extends EapSimAttribute { |
| private static final int ATTR_LENGTH = LENGTH_SCALING; |
| |
| public final int counter; |
| |
| public AtCounter(int lengthInBytes, ByteBuffer byteBuffer) |
| throws EapSimInvalidAttributeException { |
| super(EAP_AT_COUNTER, lengthInBytes); |
| |
| if (lengthInBytes != ATTR_LENGTH) { |
| throw new EapSimInvalidAttributeException("Invalid Length specified"); |
| } |
| |
| this.counter = Short.toUnsignedInt(byteBuffer.getShort()); |
| } |
| |
| @VisibleForTesting |
| public AtCounter(int counter) throws EapSimInvalidAttributeException { |
| super(EAP_AT_COUNTER, ATTR_LENGTH); |
| this.counter = counter; |
| } |
| |
| @Override |
| public void encode(ByteBuffer byteBuffer) { |
| encodeAttributeHeader(byteBuffer); |
| byteBuffer.putShort((short) counter); |
| } |
| } |
| |
| |
| /** |
| * AtCounterTooSmall represents the AT_COUNTER_TOO_SMALL attribute defined in RFC 4186 Section |
| * 10.16 |
| */ |
| public static class AtCounterTooSmall extends EapSimAttribute { |
| private static final int ATTR_LENGTH = LENGTH_SCALING; |
| private static final int ATTR_HEADER = 2; |
| |
| public AtCounterTooSmall(int lengthInBytes, ByteBuffer byteBuffer) |
| throws EapSimInvalidAttributeException { |
| super(EAP_AT_COUNTER_TOO_SMALL, lengthInBytes); |
| |
| if (lengthInBytes != ATTR_LENGTH) { |
| throw new EapSimInvalidAttributeException("Invalid Length specified"); |
| } |
| consumePadding(ATTR_HEADER, byteBuffer); |
| } |
| |
| public AtCounterTooSmall() throws EapSimInvalidAttributeException { |
| super(EAP_AT_COUNTER_TOO_SMALL, ATTR_LENGTH); |
| } |
| |
| @Override |
| public void encode(ByteBuffer byteBuffer) { |
| encodeAttributeHeader(byteBuffer); |
| addPadding(ATTR_HEADER, byteBuffer); |
| } |
| } |
| |
| /** |
| * AtNonceS represents the AT_NONCE_S attribute defined in RFC 4186 Section 10.17 |
| * |
| * <p>This Nonce is generated by the server and used for fast re-authentication only. |
| */ |
| public static class AtNonceS extends EapSimAttribute { |
| private static final int ATTR_LENGTH = 5 * LENGTH_SCALING; |
| private static final int NONCE_S_LENGTH = 4 * LENGTH_SCALING; |
| private static final int RESERVED_BYTES = 2; |
| |
| public final byte[] nonceS; |
| |
| public AtNonceS(int lengthInBytes, ByteBuffer byteBuffer) |
| throws EapSimInvalidAttributeException { |
| super(EAP_AT_NONCE_S, lengthInBytes); |
| |
| if (lengthInBytes != ATTR_LENGTH) { |
| throw new EapSimInvalidAttributeException("Invalid Length specified"); |
| } |
| |
| // next two bytes are reserved (RFC 4186 Section 10.17) |
| byteBuffer.get(new byte[RESERVED_BYTES]); |
| |
| nonceS = new byte[NONCE_S_LENGTH]; |
| byteBuffer.get(nonceS); |
| } |
| |
| @VisibleForTesting |
| public AtNonceS(byte[] nonceS) throws EapSimInvalidAttributeException { |
| super(EAP_AT_NONCE_S, ATTR_LENGTH); |
| this.nonceS = nonceS; |
| } |
| |
| @Override |
| public void encode(ByteBuffer byteBuffer) { |
| encodeAttributeHeader(byteBuffer); |
| byteBuffer.put(new byte[RESERVED_BYTES]); |
| byteBuffer.put(nonceS); |
| } |
| } |
| |
| /** |
| * AtNotification represents the AT_NOTIFICATION attribute defined in RFC 4186 Section 10.18 |
| */ |
| public static class AtNotification extends EapSimAttribute { |
| private static final int ATTR_LENGTH = 4; |
| private static final int SUCCESS_MASK = 0x8000; |
| private static final int PRE_CHALLENGE_MASK = 0x4000; |
| |
| // Notification codes defined in RFC 4186 Section 10.18 |
| public static final int GENERAL_FAILURE_POST_CHALLENGE = 0; |
| public static final int GENERAL_FAILURE_PRE_CHALLENGE = 16384; // 0x4000 |
| public static final int SUCCESS = 32768; // 0x8000 |
| public static final int DENIED_ACCESS_POST_CHALLENGE = 1026; |
| public static final int USER_NOT_SUBSCRIBED_POST_CHALLENGE = 1031; |
| |
| private static final Map<Integer, String> CODE_DEFS = loadCodeDefs(); |
| |
| public final boolean isSuccessCode; |
| public final boolean isPreChallenge; |
| public final int notificationCode; |
| |
| public AtNotification(int lengthInBytes, ByteBuffer byteBuffer) |
| throws EapSimInvalidAttributeException { |
| super(EAP_AT_NOTIFICATION, lengthInBytes); |
| |
| if (lengthInBytes != ATTR_LENGTH) { |
| throw new EapSimInvalidAttributeException("Invalid Length specified"); |
| } |
| |
| notificationCode = Short.toUnsignedInt(byteBuffer.getShort()); |
| |
| // If Success bit == 0, failure is implied |
| isSuccessCode = (notificationCode & SUCCESS_MASK) == SUCCESS_MASK; |
| |
| // if Phase bit == 0, notification code can only be used after a successful |
| isPreChallenge = (notificationCode & PRE_CHALLENGE_MASK) == PRE_CHALLENGE_MASK; |
| |
| if (isSuccessCode && isPreChallenge) { |
| throw new EapSimInvalidAttributeException("Invalid state specified"); |
| } |
| } |
| |
| @VisibleForTesting |
| public AtNotification(int notificationCode) throws EapSimInvalidAttributeException { |
| super(EAP_AT_NOTIFICATION, ATTR_LENGTH); |
| this.notificationCode = notificationCode; |
| |
| // If Success bit == 0, failure is implied |
| isSuccessCode = (notificationCode & SUCCESS_MASK) != 0; |
| |
| // if Phase bit == 0, notification code can only be used after a successful |
| isPreChallenge = (notificationCode & PRE_CHALLENGE_MASK) != 0; |
| |
| if (isSuccessCode && isPreChallenge) { |
| throw new EapSimInvalidAttributeException("Invalid state specified"); |
| } |
| } |
| |
| @Override |
| public void encode(ByteBuffer byteBuffer) { |
| encodeAttributeHeader(byteBuffer); |
| byteBuffer.putShort((short) notificationCode); |
| } |
| |
| @Override |
| public String toString() { |
| String description = CODE_DEFS.getOrDefault(notificationCode, "Code not recognized"); |
| return "{Notification Code=" + notificationCode + ", descr=" + description + "}"; |
| } |
| |
| private static Map<Integer, String> loadCodeDefs() { |
| Map<Integer, String> defs = new HashMap<>(); |
| defs.put(GENERAL_FAILURE_POST_CHALLENGE, |
| "General failure after authentication. (Implies failure, used after successful" |
| + " authentication.)"); |
| defs.put(GENERAL_FAILURE_PRE_CHALLENGE, |
| "General failure. (Implies failure, used before authentication.)"); |
| defs.put(SUCCESS, |
| "Success. User has been successfully authenticated. (Does not imply failure," |
| + " used after successful authentication)."); |
| defs.put(DENIED_ACCESS_POST_CHALLENGE, |
| "User has been temporarily denied access to the requested service. (Implies" |
| + " failure, used after successful authentication.)"); |
| defs.put(USER_NOT_SUBSCRIBED_POST_CHALLENGE, |
| "User has not subscribed to the requested service. (Implies failure, used" |
| + " after successful authentication.)"); |
| return defs; |
| } |
| } |
| |
| /** |
| * AtClientErrorCode represents the AT_CLIENT_ERROR_CODE attribute defined in RFC 4186 Section |
| * 10.19 |
| */ |
| public static class AtClientErrorCode extends EapSimAttribute { |
| private static final String TAG = AtClientErrorCode.class.getSimpleName(); |
| private static final int ATTR_LENGTH = 4; |
| |
| // Error codes defined in RFC 4186 Section 10.19 |
| public static final AtClientErrorCode UNABLE_TO_PROCESS = getClientErrorCode(0); |
| public static final AtClientErrorCode UNSUPPORTED_VERSION = getClientErrorCode(1); |
| public static final AtClientErrorCode INSUFFICIENT_CHALLENGES = getClientErrorCode(2); |
| public static final AtClientErrorCode STALE_RANDS = getClientErrorCode(3); |
| |
| public final int errorCode; |
| |
| public AtClientErrorCode(int lengthInBytes, int errorCode) |
| throws EapSimInvalidAttributeException { |
| super(EAP_AT_CLIENT_ERROR_CODE, lengthInBytes); |
| |
| if (lengthInBytes != ATTR_LENGTH) { |
| throw new EapSimInvalidAttributeException("Invalid Length specified"); |
| } |
| |
| this.errorCode = errorCode; |
| } |
| |
| @Override |
| public void encode(ByteBuffer byteBuffer) { |
| encodeAttributeHeader(byteBuffer); |
| byteBuffer.putShort((short) errorCode); |
| } |
| |
| private static AtClientErrorCode getClientErrorCode(int errorCode) { |
| try { |
| return new AtClientErrorCode(ATTR_LENGTH, errorCode); |
| } catch (EapSimInvalidAttributeException exception) { |
| Log.wtf(TAG, "Exception thrown while making AtClientErrorCodeConstants"); |
| return null; |
| } |
| } |
| } |
| } |