1 /*
2  * Copyright (C) 2018 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.IkeDerAsn1DnIdentification;
20 import android.net.ipsec.ike.IkeFqdnIdentification;
21 import android.net.ipsec.ike.IkeIdentification;
22 import android.net.ipsec.ike.IkeIpv4AddrIdentification;
23 import android.net.ipsec.ike.IkeIpv6AddrIdentification;
24 import android.net.ipsec.ike.IkeKeyIdIdentification;
25 import android.net.ipsec.ike.IkeRfc822AddrIdentification;
26 import android.net.ipsec.ike.exceptions.IkeProtocolException;
27 
28 import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException;
29 import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException;
30 
31 import java.nio.ByteBuffer;
32 import java.security.cert.X509Certificate;
33 
34 /**
35  * IkeIdPayload represents an Identification Initiator Payload or an Identification Responder
36  * Payload.
37  *
38  * <p>Identification Initiator Payload and Identification Responder Payload have same format but
39  * different payload type.
40  *
41  * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.5">RFC 7296, Internet Key Exchange
42  *     Protocol Version 2 (IKEv2)</a>
43  */
44 public final class IkeIdPayload extends IkePayload {
45     // Length of ID Payload header in octets.
46     private static final int ID_HEADER_LEN = 4;
47     // Length of reserved field in octets.
48     private static final int ID_HEADER_RESERVED_LEN = 3;
49 
50     public final IkeIdentification ikeId;
51 
52     /**
53      * Construct IkeIdPayload for received IKE packet in the context of {@link IkePayloadFactory}.
54      *
55      * @param critical indicates if it is a critical payload.
56      * @param payloadBody payload body in byte array.
57      * @param isInitiator indicates whether this payload contains the ID of IKE initiator or IKE
58      *     responder.
59      * @throws IkeProtocolException for decoding error.
60      */
IkeIdPayload(boolean critical, byte[] payloadBody, boolean isInitiator)61     IkeIdPayload(boolean critical, byte[] payloadBody, boolean isInitiator)
62             throws IkeProtocolException {
63         super((isInitiator ? PAYLOAD_TYPE_ID_INITIATOR : PAYLOAD_TYPE_ID_RESPONDER), critical);
64         // TODO: b/119791832 Add helper method for checking payload body length in superclass.
65         if (payloadBody.length <= ID_HEADER_LEN) {
66             throw new InvalidSyntaxException(getTypeString() + " is too short.");
67         }
68 
69         ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody);
70         int idType = Byte.toUnsignedInt(inputBuffer.get());
71 
72         // Skip reserved field
73         inputBuffer.get(new byte[ID_HEADER_RESERVED_LEN]);
74 
75         byte[] idData = new byte[payloadBody.length - ID_HEADER_LEN];
76         inputBuffer.get(idData);
77 
78         switch (idType) {
79             case IkeIdentification.ID_TYPE_IPV4_ADDR:
80                 ikeId = new IkeIpv4AddrIdentification(idData);
81                 return;
82             case IkeIdentification.ID_TYPE_FQDN:
83                 ikeId = new IkeFqdnIdentification(idData);
84                 return;
85             case IkeIdentification.ID_TYPE_RFC822_ADDR:
86                 ikeId = new IkeRfc822AddrIdentification(idData);
87                 return;
88             case IkeIdentification.ID_TYPE_IPV6_ADDR:
89                 ikeId = new IkeIpv6AddrIdentification(idData);
90                 return;
91             case IkeIdentification.ID_TYPE_DER_ASN1_DN:
92                 ikeId = new IkeDerAsn1DnIdentification(idData);
93                 return;
94             case IkeIdentification.ID_TYPE_KEY_ID:
95                 ikeId = new IkeKeyIdIdentification(idData);
96                 return;
97             default:
98                 throw new AuthenticationFailedException("Unsupported ID type: " + idType);
99         }
100     }
101 
102     /**
103      * Construct IkeIdPayload for an outbound IKE packet.
104      *
105      * @param isInitiator indicates whether this payload contains the ID of IKE initiator or IKE
106      *     responder.
107      * @param ikeId the IkeIdentification.
108      */
IkeIdPayload(boolean isInitiator, IkeIdentification ikeId)109     public IkeIdPayload(boolean isInitiator, IkeIdentification ikeId) {
110         super((isInitiator ? PAYLOAD_TYPE_ID_INITIATOR : PAYLOAD_TYPE_ID_RESPONDER), false);
111         this.ikeId = ikeId;
112     }
113 
114     /**
115      * Get encoded ID payload body for building or validating an Auth Payload.
116      *
117      * @return the byte array of encoded ID payload body.
118      */
getEncodedPayloadBody()119     public byte[] getEncodedPayloadBody() {
120         ByteBuffer byteBuffer = ByteBuffer.allocate(getPayloadLength() - GENERIC_HEADER_LENGTH);
121 
122         byteBuffer
123                 .put((byte) ikeId.idType)
124                 .put(new byte[ID_HEADER_RESERVED_LEN])
125                 .put(ikeId.getEncodedIdData());
126         return byteBuffer.array();
127     }
128 
129     /** Validate if the end certificate matches the ID */
validateEndCertIdOrThrow(X509Certificate endCert)130     public void validateEndCertIdOrThrow(X509Certificate endCert)
131             throws AuthenticationFailedException {
132         ikeId.validateEndCertIdOrThrow(endCert);
133     }
134 
135     /**
136      * Encode Identification Payload to ByteBuffer.
137      *
138      * @param nextPayload type of payload that follows this payload.
139      * @param byteBuffer destination ByteBuffer that stores encoded payload.
140      */
141     @Override
encodeToByteBuffer(@ayloadType int nextPayload, ByteBuffer byteBuffer)142     protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) {
143         encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer);
144         byteBuffer.put(getEncodedPayloadBody());
145     }
146 
147     /**
148      * Get entire payload length.
149      *
150      * @return entire payload length.
151      */
152     @Override
getPayloadLength()153     protected int getPayloadLength() {
154         return GENERIC_HEADER_LENGTH + ID_HEADER_LEN + ikeId.getEncodedIdData().length;
155     }
156 
157     /**
158      * Return the payload type as a String.
159      *
160      * @return the payload type as a String.
161      */
162     @Override
getTypeString()163     public String getTypeString() {
164         switch (payloadType) {
165             case PAYLOAD_TYPE_ID_INITIATOR:
166                 return "IDi";
167             case PAYLOAD_TYPE_ID_RESPONDER:
168                 return "IDr";
169             default:
170                 // Won't reach here.
171                 throw new IllegalArgumentException(
172                         "Invalid Payload Type for Identification Payload.");
173         }
174     }
175 }
176