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.ipsec.ike.message;
18 
19 import android.annotation.Nullable;
20 import android.net.ipsec.ike.exceptions.IkeProtocolException;
21 
22 import com.android.internal.annotations.VisibleForTesting;
23 import com.android.internal.net.ipsec.ike.crypto.IkeCipher;
24 import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity;
25 import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException;
26 
27 import java.nio.ByteBuffer;
28 import java.security.GeneralSecurityException;
29 
30 /**
31  * IkeSkfPayload represents an Encrypted and Authenticated Fragment Payload.
32  *
33  * @see <a href="https://tools.ietf.org/html/rfc7383">RFC 7383, Internet Key Exchange Protocol
34  *     Version 2 (IKEv2) Message Fragmentation</a>
35  */
36 public final class IkeSkfPayload extends IkeSkPayload {
37     public static final int SKF_HEADER_LEN = 4;
38 
39     /** Current Fragment message number, starting from 1 */
40     public final int fragmentNum;
41     /** Number of Fragment messages into which the original message was divided */
42     public final int totalFragments;
43 
44     /**
45      * Construct an instance of IkeSkfPayload by authenticating and decrypting an incoming packet.
46      *
47      * <p>SKF Payload with invalid fragmentNum or invalid totalFragments, or cannot be authenticated
48      * or decrypted MUST be discarded
49      *
50      * @param critical indicates if it is a critical payload.
51      * @param message the byte array contains the whole IKE message.
52      * @param integrityMac the negotiated integrity algorithm.
53      * @param decryptCipher the negotiated encryption algorithm.
54      * @param integrityKey the negotiated integrity algorithm key.
55      * @param decryptionKey the negotiated decryption key.
56      */
IkeSkfPayload( boolean critical, byte[] message, @Nullable IkeMacIntegrity integrityMac, IkeCipher decryptCipher, byte[] integrityKey, byte[] decryptionKey)57     IkeSkfPayload(
58             boolean critical,
59             byte[] message,
60             @Nullable IkeMacIntegrity integrityMac,
61             IkeCipher decryptCipher,
62             byte[] integrityKey,
63             byte[] decryptionKey)
64             throws IkeProtocolException, GeneralSecurityException {
65         super(
66                 true /*isSkf*/,
67                 critical,
68                 IkeHeader.IKE_HEADER_LENGTH + GENERIC_HEADER_LENGTH + SKF_HEADER_LEN,
69                 message,
70                 integrityMac,
71                 decryptCipher,
72                 integrityKey,
73                 decryptionKey);
74 
75         // TODO: Support constructing IkeEncryptedPayloadBody using AEAD.
76 
77         ByteBuffer inputBuffer = ByteBuffer.wrap(message);
78         inputBuffer.get(new byte[IkeHeader.IKE_HEADER_LENGTH + GENERIC_HEADER_LENGTH]);
79 
80         fragmentNum = Short.toUnsignedInt(inputBuffer.getShort());
81         totalFragments = Short.toUnsignedInt(inputBuffer.getShort());
82 
83         if (fragmentNum < 1 || totalFragments < 1 || fragmentNum > totalFragments) {
84             throw new InvalidSyntaxException(
85                     "Received invalid Fragment Number or Total Fragments Number. Fragment Number: "
86                             + fragmentNum
87                             + "  Total Fragments: "
88                             + totalFragments);
89         }
90     }
91 
92     /**
93      * Construct an instance of IkeSkfPayload for building outbound packet.
94      *
95      * @param ikeHeader the IKE header.
96      * @param firstPayloadType the type of first payload nested in SkPayload.
97      * @param unencryptedPayloads the encoded payload list to protect.
98      * @param integrityMac the negotiated integrity algorithm.
99      * @param encryptCipher the negotiated encryption algorithm.
100      * @param integrityKey the negotiated integrity algorithm key.
101      * @param encryptionKey the negotiated encryption key.
102      */
IkeSkfPayload( IkeHeader ikeHeader, @PayloadType int firstPayloadType, byte[] unencryptedPayloads, @Nullable IkeMacIntegrity integrityMac, IkeCipher encryptCipher, byte[] integrityKey, byte[] encryptionKey, int fragNum, int totalFrags)103     IkeSkfPayload(
104             IkeHeader ikeHeader,
105             @PayloadType int firstPayloadType,
106             byte[] unencryptedPayloads,
107             @Nullable IkeMacIntegrity integrityMac,
108             IkeCipher encryptCipher,
109             byte[] integrityKey,
110             byte[] encryptionKey,
111             int fragNum,
112             int totalFrags) {
113         super(
114                 ikeHeader,
115                 firstPayloadType,
116                 encodeSkfHeader(fragNum, totalFrags),
117                 unencryptedPayloads,
118                 integrityMac,
119                 encryptCipher,
120                 integrityKey,
121                 encryptionKey);
122         fragmentNum = fragNum;
123         totalFragments = totalFrags;
124     }
125 
126     /** Construct an instance of IkeSkfPayload for testing. */
127     @VisibleForTesting
IkeSkfPayload(IkeEncryptedPayloadBody encryptedPayloadBody, int fragNum, int totalFrags)128     IkeSkfPayload(IkeEncryptedPayloadBody encryptedPayloadBody, int fragNum, int totalFrags) {
129         super(true /*isSkf*/, encryptedPayloadBody);
130         fragmentNum = fragNum;
131         totalFragments = totalFrags;
132     }
133 
134     @VisibleForTesting
encodeSkfHeader(int fragNum, int totalFrags)135     static byte[] encodeSkfHeader(int fragNum, int totalFrags) {
136         ByteBuffer buffer = ByteBuffer.allocate(SKF_HEADER_LEN);
137         buffer.putShort((short) fragNum).putShort((short) totalFrags);
138         return buffer.array();
139     }
140 
141     /**
142      * Encode this payload to a ByteBuffer.
143      *
144      * @param nextPayload type of payload that follows this payload.
145      * @param byteBuffer destination ByteBuffer that stores encoded payload.
146      */
147     @Override
encodeToByteBuffer(@ayloadType int nextPayload, ByteBuffer byteBuffer)148     protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) {
149         encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer);
150         byteBuffer
151                 .putShort((short) fragmentNum)
152                 .putShort((short) totalFragments)
153                 .put(mIkeEncryptedPayloadBody.encode());
154     }
155 
156     /**
157      * Get entire payload length.
158      *
159      * @return entire payload length.
160      */
161     @Override
getPayloadLength()162     protected int getPayloadLength() {
163         return GENERIC_HEADER_LENGTH + SKF_HEADER_LEN + mIkeEncryptedPayloadBody.getLength();
164     }
165 
166     /**
167      * Return the payload type as a String.
168      *
169      * @return the payload type as a String.
170      */
171     @Override
getTypeString()172     public String getTypeString() {
173         return "SKF";
174     }
175 }
176