| /* |
| * 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.ikev2.message; |
| |
| import android.annotation.Nullable; |
| |
| import com.android.ike.ikev2.crypto.IkeCipher; |
| import com.android.ike.ikev2.crypto.IkeMacIntegrity; |
| import com.android.ike.ikev2.exceptions.IkeProtocolException; |
| import com.android.ike.ikev2.exceptions.InvalidSyntaxException; |
| import com.android.internal.annotations.VisibleForTesting; |
| |
| import java.nio.ByteBuffer; |
| import java.security.GeneralSecurityException; |
| |
| /** |
| * IkeSkfPayload represents an Encrypted and Authenticated Fragment Payload. |
| * |
| * @see <a href="https://tools.ietf.org/html/rfc7383">RFC 7383, Internet Key Exchange Protocol |
| * Version 2 (IKEv2) Message Fragmentation</a> |
| */ |
| public final class IkeSkfPayload extends IkeSkPayload { |
| public static final int SKF_HEADER_LEN = 4; |
| |
| /** Current Fragment message number, starting from 1 */ |
| public final int fragmentNum; |
| /** Number of Fragment messages into which the original message was divided */ |
| public final int totalFragments; |
| |
| /** |
| * Construct an instance of IkeSkfPayload by authenticating and decrypting an incoming packet. |
| * |
| * <p>SKF Payload with invalid fragmentNum or invalid totalFragments, or cannot be authenticated |
| * or decrypted MUST be discarded |
| * |
| * @param critical indicates if it is a critical 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. |
| */ |
| IkeSkfPayload( |
| boolean critical, |
| byte[] message, |
| @Nullable IkeMacIntegrity integrityMac, |
| IkeCipher decryptCipher, |
| byte[] integrityKey, |
| byte[] decryptionKey) |
| throws IkeProtocolException, GeneralSecurityException { |
| super( |
| true /*isSkf*/, |
| critical, |
| IkeHeader.IKE_HEADER_LENGTH + GENERIC_HEADER_LENGTH + SKF_HEADER_LEN, |
| message, |
| integrityMac, |
| decryptCipher, |
| integrityKey, |
| decryptionKey); |
| |
| // TODO: Support constructing IkeEncryptedPayloadBody using AEAD. |
| |
| ByteBuffer inputBuffer = ByteBuffer.wrap(message); |
| inputBuffer.get(new byte[IkeHeader.IKE_HEADER_LENGTH + GENERIC_HEADER_LENGTH]); |
| |
| fragmentNum = Short.toUnsignedInt(inputBuffer.getShort()); |
| totalFragments = Short.toUnsignedInt(inputBuffer.getShort()); |
| |
| if (fragmentNum < 1 || totalFragments < 1 || fragmentNum > totalFragments) { |
| throw new InvalidSyntaxException( |
| "Received invalid Fragment Number or Total Fragments Number. Fragment Number: " |
| + fragmentNum |
| + " Total Fragments: " |
| + totalFragments); |
| } |
| } |
| |
| /** |
| * Construct an instance of IkeSkfPayload for building outbound packet. |
| * |
| * @param ikeHeader the IKE header. |
| * @param firstPayloadType the type of first payload nested in SkPayload. |
| * @param unencryptedPayloads the encoded payload list to protect. |
| * @param integrityMac the negotiated integrity algorithm. |
| * @param encryptCipher the negotiated encryption algorithm. |
| * @param integrityKey the negotiated integrity algorithm key. |
| * @param encryptionKey the negotiated encryption key. |
| */ |
| IkeSkfPayload( |
| IkeHeader ikeHeader, |
| @PayloadType int firstPayloadType, |
| byte[] unencryptedPayloads, |
| @Nullable IkeMacIntegrity integrityMac, |
| IkeCipher encryptCipher, |
| byte[] integrityKey, |
| byte[] encryptionKey, |
| int fragNum, |
| int totalFrags) { |
| super( |
| ikeHeader, |
| firstPayloadType, |
| encodeSkfHeader(fragNum, totalFrags), |
| unencryptedPayloads, |
| integrityMac, |
| encryptCipher, |
| integrityKey, |
| encryptionKey); |
| fragmentNum = fragNum; |
| totalFragments = totalFrags; |
| } |
| |
| /** Construct an instance of IkeSkfPayload for testing. */ |
| @VisibleForTesting |
| IkeSkfPayload(IkeEncryptedPayloadBody encryptedPayloadBody, int fragNum, int totalFrags) { |
| super(true /*isSkf*/, encryptedPayloadBody); |
| fragmentNum = fragNum; |
| totalFragments = totalFrags; |
| } |
| |
| @VisibleForTesting |
| static byte[] encodeSkfHeader(int fragNum, int totalFrags) { |
| ByteBuffer buffer = ByteBuffer.allocate(SKF_HEADER_LEN); |
| buffer.putShort((short) fragNum).putShort((short) totalFrags); |
| return buffer.array(); |
| } |
| |
| /** |
| * Encode this payload to a ByteBuffer. |
| * |
| * @param nextPayload type of payload that follows this payload. |
| * @param byteBuffer destination ByteBuffer that stores encoded payload. |
| */ |
| @Override |
| protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) { |
| encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer); |
| byteBuffer |
| .putShort((short) fragmentNum) |
| .putShort((short) totalFragments) |
| .put(mIkeEncryptedPayloadBody.encode()); |
| } |
| |
| /** |
| * Get entire payload length. |
| * |
| * @return entire payload length. |
| */ |
| @Override |
| protected int getPayloadLength() { |
| return GENERIC_HEADER_LENGTH + SKF_HEADER_LEN + mIkeEncryptedPayloadBody.getLength(); |
| } |
| |
| /** |
| * Return the payload type as a String. |
| * |
| * @return the payload type as a String. |
| */ |
| @Override |
| public String getTypeString() { |
| return "SKF"; |
| } |
| } |