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.net.ipsec.ike.IkeTrafficSelector;
20 import android.net.ipsec.ike.exceptions.IkeProtocolException;
21 import android.net.ipsec.ike.exceptions.InvalidSyntaxException;
22 
23 import java.nio.ByteBuffer;
24 
25 /**
26  * IkeTsPayload represents an Traffic Selector Initiator Payload or an Traffic Selector Responder
27  * Payload.
28  *
29  * <p>Traffic Selector Initiator Payload and Traffic Selector Responder Payload have same format but
30  * different payload types. They describe the address ranges and port ranges of Child SA initiator
31  * and Child SA responder.
32  *
33  * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.13">RFC 7296, Internet Key Exchange
34  *     Protocol Version 2 (IKEv2)</a>
35  */
36 public final class IkeTsPayload extends IkePayload {
37     // Length of Traffic Selector Payload header.
38     private static final int TS_HEADER_LEN = 4;
39     // Length of reserved field in octets.
40     private static final int TS_HEADER_RESERVED_LEN = 3;
41 
42     /** Number of Traffic Selectors */
43     public final int numTs;
44     /** Array of Traffic Selectors */
45     public final IkeTrafficSelector[] trafficSelectors;
46 
IkeTsPayload(boolean critical, byte[] payloadBody, boolean isInitiator)47     IkeTsPayload(boolean critical, byte[] payloadBody, boolean isInitiator)
48             throws IkeProtocolException {
49         super((isInitiator ? PAYLOAD_TYPE_TS_INITIATOR : PAYLOAD_TYPE_TS_RESPONDER), critical);
50 
51         ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody);
52         numTs = Byte.toUnsignedInt(inputBuffer.get());
53         if (numTs == 0) {
54             throw new InvalidSyntaxException("Cannot find Traffic Selector in TS payload.");
55         }
56 
57         // Skip RESERVED byte
58         inputBuffer.get(new byte[TS_HEADER_RESERVED_LEN]);
59 
60         // Decode Traffic Selectors
61         byte[] tsBytes = new byte[inputBuffer.remaining()];
62         inputBuffer.get(tsBytes);
63         trafficSelectors = IkeTrafficSelector.decodeIkeTrafficSelectors(numTs, tsBytes);
64     }
65 
66     /**
67      * Construct an instance of IkeTsPayload for building an outbound IKE message.
68      *
69      * @param isInitiator indicates if this payload is for a Child SA initiator or responder.
70      * @param ikeTrafficSelectors the array of included traffic selectors.
71      */
IkeTsPayload(boolean isInitiator, IkeTrafficSelector[] ikeTrafficSelectors)72     public IkeTsPayload(boolean isInitiator, IkeTrafficSelector[] ikeTrafficSelectors) {
73         super((isInitiator ? PAYLOAD_TYPE_TS_INITIATOR : PAYLOAD_TYPE_TS_RESPONDER), false);
74 
75         if (ikeTrafficSelectors == null || ikeTrafficSelectors.length == 0) {
76             throw new IllegalArgumentException(
77                     "TS Payload requires at least one Traffic Selector.");
78         }
79 
80         numTs = ikeTrafficSelectors.length;
81         trafficSelectors = ikeTrafficSelectors;
82     }
83 
84     /**
85      * Check if this TS payload contains the all TS in the provided TS payload.
86      *
87      * <p>A TS response cannot be narrower than a TS request. When doing rekey, the newly negotiated
88      * TS cannot be narrower than old negotiated TS.
89      *
90      * <p>This method will be used to (1) validate that an inbound response is subset of a locally
91      * generated request; and (2) validate that an inbound rekey request/response is superset of
92      * current negotiated TS.
93      *
94      * @param tsPayload the other TS payload to validate
95      * @return true if current TS Payload contains all TS in the input tsPayload
96      */
contains(IkeTsPayload tsPayload)97     public boolean contains(IkeTsPayload tsPayload) {
98         subTsLoop:
99         for (IkeTrafficSelector subTs : tsPayload.trafficSelectors) {
100             for (IkeTrafficSelector superTs : this.trafficSelectors) {
101                 if (superTs.contains(subTs)) {
102                     continue subTsLoop;
103                 }
104             }
105             return false;
106         }
107         return true;
108     }
109 
110     /**
111      * Encode Traffic Selector Payload to ByteBuffer.
112      *
113      * @param nextPayload type of payload that follows this payload.
114      * @param byteBuffer destination ByteBuffer that stores encoded payload.
115      */
116     @Override
encodeToByteBuffer(@ayloadType int nextPayload, ByteBuffer byteBuffer)117     protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) {
118         encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer);
119 
120         byteBuffer.put((byte) numTs).put(new byte[TS_HEADER_RESERVED_LEN]);
121         for (IkeTrafficSelector ts : trafficSelectors) {
122             ts.encodeToByteBuffer(byteBuffer);
123         }
124     }
125 
126     /**
127      * Get entire payload length.
128      *
129      * @return entire payload length.
130      */
131     @Override
getPayloadLength()132     protected int getPayloadLength() {
133         int len = GENERIC_HEADER_LENGTH + TS_HEADER_LEN;
134         for (IkeTrafficSelector ts : trafficSelectors) {
135             len += ts.selectorLength;
136         }
137 
138         return len;
139     }
140 
141     /**
142      * Return the payload type as a String.
143      *
144      * @return the payload type as a String.
145      */
146     @Override
getTypeString()147     public String getTypeString() {
148         switch (payloadType) {
149             case PAYLOAD_TYPE_TS_INITIATOR:
150                 return "TSi";
151             case PAYLOAD_TYPE_TS_RESPONDER:
152                 return "TSr";
153             default:
154                 // Won't reach here.
155                 throw new IllegalArgumentException(
156                         "Invalid Payload Type for Traffic Selector Payload.");
157         }
158     }
159 }
160