aboutsummaryrefslogtreecommitdiff
path: root/src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java')
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java244
1 files changed, 244 insertions, 0 deletions
diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java b/src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java
new file mode 100644
index 00000000..2f191d4e
--- /dev/null
+++ b/src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2018 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.internal.net.ipsec.ike.message;
+
+import android.annotation.Nullable;
+import android.net.ipsec.ike.exceptions.IkeProtocolException;
+import android.util.Pair;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.net.ipsec.ike.crypto.IkeCipher;
+import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity;
+import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException;
+
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+
+/**
+ * IkePayloadFactory is used for creating IkePayload according to is type.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc7296#section-3">RFC 7296, Internet Key Exchange
+ * Protocol Version 2 (IKEv2)</a>
+ */
+final class IkePayloadFactory {
+
+ // Critical bit is set and following reserved 7 bits are unset.
+ private static final byte PAYLOAD_HEADER_CRITICAL_BIT_SET = (byte) 0x80;
+
+ private static boolean isCriticalPayload(byte flagByte) {
+ // Reserved 7 bits following critical bit must be ignore on receipt.
+ return (flagByte & PAYLOAD_HEADER_CRITICAL_BIT_SET) == PAYLOAD_HEADER_CRITICAL_BIT_SET;
+ }
+
+ /** Default IIkePayloadDecoder instance used for constructing IkePayload */
+ static IIkePayloadDecoder sDecoderInstance = new IkePayloadDecoder();
+
+ /**
+ * IkePayloadDecoder implements IIkePayloadDecoder for constructing IkePayload from decoding
+ * received message.
+ *
+ * <p>Package private
+ */
+ @VisibleForTesting
+ static class IkePayloadDecoder implements IIkePayloadDecoder {
+ @Override
+ public IkePayload decodeIkePayload(
+ int payloadType, boolean isCritical, boolean isResp, byte[] payloadBody)
+ throws IkeProtocolException {
+ switch (payloadType) {
+ // TODO: Add cases for creating supported payloads.
+ case IkePayload.PAYLOAD_TYPE_SA:
+ return new IkeSaPayload(isCritical, isResp, payloadBody);
+ case IkePayload.PAYLOAD_TYPE_KE:
+ return new IkeKePayload(isCritical, payloadBody);
+ case IkePayload.PAYLOAD_TYPE_ID_INITIATOR:
+ return new IkeIdPayload(isCritical, payloadBody, true);
+ case IkePayload.PAYLOAD_TYPE_ID_RESPONDER:
+ return new IkeIdPayload(isCritical, payloadBody, false);
+ case IkePayload.PAYLOAD_TYPE_CERT:
+ return IkeCertPayload.getIkeCertPayload(isCritical, payloadBody);
+ case IkePayload.PAYLOAD_TYPE_AUTH:
+ return IkeAuthPayload.getIkeAuthPayload(isCritical, payloadBody);
+ case IkePayload.PAYLOAD_TYPE_NONCE:
+ return new IkeNoncePayload(isCritical, payloadBody);
+ case IkePayload.PAYLOAD_TYPE_NOTIFY:
+ return new IkeNotifyPayload(isCritical, payloadBody);
+ case IkePayload.PAYLOAD_TYPE_DELETE:
+ return new IkeDeletePayload(isCritical, payloadBody);
+ case IkePayload.PAYLOAD_TYPE_VENDOR:
+ return new IkeVendorPayload(isCritical, payloadBody);
+ case IkePayload.PAYLOAD_TYPE_TS_INITIATOR:
+ return new IkeTsPayload(isCritical, payloadBody, true);
+ case IkePayload.PAYLOAD_TYPE_TS_RESPONDER:
+ return new IkeTsPayload(isCritical, payloadBody, false);
+ case IkePayload.PAYLOAD_TYPE_CP:
+ return new IkeConfigPayload(isCritical, payloadBody);
+ case IkePayload.PAYLOAD_TYPE_EAP:
+ return new IkeEapPayload(isCritical, payloadBody);
+ default:
+ return new IkeUnsupportedPayload(payloadType, isCritical);
+ }
+ }
+
+ @Override
+ public IkeSkPayload decodeIkeSkPayload(
+ boolean isSkf,
+ boolean critical,
+ byte[] message,
+ @Nullable IkeMacIntegrity integrityMac,
+ IkeCipher decryptCipher,
+ byte[] integrityKey,
+ byte[] decryptionKey)
+ throws IkeProtocolException, GeneralSecurityException {
+ if (isSkf) {
+ return new IkeSkfPayload(
+ critical,
+ message,
+ integrityMac,
+ decryptCipher,
+ integrityKey,
+ decryptionKey);
+ } else {
+ return new IkeSkPayload(
+ critical,
+ message,
+ integrityMac,
+ decryptCipher,
+ integrityKey,
+ decryptionKey);
+ }
+ }
+ }
+
+ /**
+ * Construct an instance of IkePayload according to its payload type.
+ *
+ * @param payloadType the current payload type. All supported types will fall in {@link
+ * IkePayload.PayloadType}
+ * @param input the encoded IKE message body containing all payloads. Position of it will
+ * increment.
+ * @return a Pair including IkePayload and next payload type.
+ */
+ protected static Pair<IkePayload, Integer> getIkePayload(
+ int payloadType, boolean isResp, ByteBuffer input) throws IkeProtocolException {
+ int nextPayloadType = (int) input.get();
+ // read critical bit
+ boolean isCritical = isCriticalPayload(input.get());
+
+ int payloadLength = Short.toUnsignedInt(input.getShort());
+ if (payloadLength <= IkePayload.GENERIC_HEADER_LENGTH) {
+ throw new InvalidSyntaxException(
+ "Invalid Payload Length: Payload length is too short.");
+ }
+ int bodyLength = payloadLength - IkePayload.GENERIC_HEADER_LENGTH;
+ if (bodyLength > input.remaining()) {
+ // It is not clear whether previous payloads or current payload has invalid payload
+ // length.
+ throw new InvalidSyntaxException("Invalid Payload Length: Payload length is too long.");
+ }
+ byte[] payloadBody = new byte[bodyLength];
+ input.get(payloadBody);
+
+ IkePayload payload =
+ sDecoderInstance.decodeIkePayload(payloadType, isCritical, isResp, payloadBody);
+ return new Pair(payload, nextPayloadType);
+ }
+
+ /**
+ * Construct an instance of IkeSkPayload by decrypting the received message.
+ *
+ * @param isSkf indicates if this is a SKF Payload.
+ * @param message the byte array contains the whole IKE message.
+ * @param integrityMac the negotiated integrity algorithm.
+ * @param decryptCipher the negotiated encryption algorithm.
+ * @param integrityKey the negotiated integrity algorithm key.
+ * @param decryptionKey the negotiated decryption key.
+ * @return a pair including IkePayload and next payload type.
+ * @throws IkeProtocolException for decoding errors.
+ * @throws GeneralSecurityException if there is any error during integrity check or decryption.
+ */
+ protected static Pair<IkeSkPayload, Integer> getIkeSkPayload(
+ boolean isSkf,
+ byte[] message,
+ IkeMacIntegrity integrityMac,
+ IkeCipher decryptCipher,
+ byte[] integrityKey,
+ byte[] decryptionKey)
+ throws IkeProtocolException, GeneralSecurityException {
+ ByteBuffer input =
+ ByteBuffer.wrap(
+ message,
+ IkeHeader.IKE_HEADER_LENGTH,
+ message.length - IkeHeader.IKE_HEADER_LENGTH);
+
+ int nextPayloadType = (int) input.get();
+ // read critical bit
+ boolean isCritical = isCriticalPayload(input.get());
+
+ int payloadLength = Short.toUnsignedInt(input.getShort());
+
+ int bodyLength = message.length - IkeHeader.IKE_HEADER_LENGTH;
+ if (bodyLength < payloadLength) {
+ throw new InvalidSyntaxException(
+ "Invalid length of SK Payload: Payload length is too long.");
+ } else if (bodyLength > payloadLength) {
+ // According to RFC 7296, SK Payload must be the last payload and for CREATE_CHILD_SA,
+ // IKE_AUTH and INFORMATIONAL exchanges, message following the header is encrypted. Thus
+ // this implementaion only accepts that SK Payload to be the only payload. Any IKE
+ // packet violating this format will be treated as invalid. A request violating this
+ // format will be rejected and replied with an error notification.
+ throw new InvalidSyntaxException(
+ "Invalid length of SK Payload: Payload length is too short"
+ + " or SK Payload is not the only payload.");
+ }
+
+ IkeSkPayload payload =
+ sDecoderInstance.decodeIkeSkPayload(
+ isSkf,
+ isCritical,
+ message,
+ integrityMac,
+ decryptCipher,
+ integrityKey,
+ decryptionKey);
+
+ return new Pair(payload, nextPayloadType);
+ }
+
+ /**
+ * IIkePayloadDecoder provides a package private interface for constructing IkePayload from
+ * decoding received message.
+ *
+ * <p>IIkePayloadDecoder exists so that the interface is injectable for testing.
+ */
+ @VisibleForTesting
+ interface IIkePayloadDecoder {
+ IkePayload decodeIkePayload(
+ int payloadType, boolean isCritical, boolean isResp, byte[] payloadBody)
+ throws IkeProtocolException;
+
+ IkeSkPayload decodeIkeSkPayload(
+ boolean isSkf,
+ boolean critical,
+ byte[] message,
+ @Nullable IkeMacIntegrity integrityMac,
+ IkeCipher decryptCipher,
+ byte[] integrityKey,
+ byte[] decryptionKey)
+ throws IkeProtocolException, GeneralSecurityException;
+ }
+}