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 static android.net.ipsec.ike.IkeManager.getIkeLog;
20 
21 import android.annotation.NonNull;
22 import android.annotation.StringDef;
23 import android.net.ipsec.ike.exceptions.AuthenticationFailedException;
24 import android.net.ipsec.ike.exceptions.IkeProtocolException;
25 import android.net.ipsec.ike.exceptions.InvalidSyntaxException;
26 import android.util.ArraySet;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 import com.android.internal.net.ipsec.ike.crypto.IkeMacPrf;
30 import com.android.internal.net.ipsec.ike.message.IkeAuthPayload.AuthMethod;
31 
32 import java.lang.annotation.Retention;
33 import java.lang.annotation.RetentionPolicy;
34 import java.nio.ByteBuffer;
35 import java.security.InvalidKeyException;
36 import java.security.NoSuchAlgorithmException;
37 import java.security.PrivateKey;
38 import java.security.ProviderException;
39 import java.security.Signature;
40 import java.security.SignatureException;
41 import java.security.cert.X509Certificate;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.Collections;
45 import java.util.HashMap;
46 import java.util.List;
47 import java.util.Map;
48 import java.util.Set;
49 
50 /**
51  * IkeAuthDigitalSignPayload represents Authentication Payload using a specific or generic digital
52  * signature authentication method.
53  *
54  * <p>If AUTH_METHOD_RSA_DIGITAL_SIGN is used, then the hash algorithm is SHA1. If
55  * AUTH_METHOD_GENERIC_DIGITAL_SIGN is used, the signature algorithm and hash algorithm are
56  * extracted from authentication data.
57  *
58  * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.8">RFC 7296, Internet Key Exchange
59  *     Protocol Version 2 (IKEv2)</a>
60  * @see <a href="https://tools.ietf.org/html/rfc7427">RFC 7427, Signature Authentication in the
61  *     Internet Key Exchange Version 2 (IKEv2)</a>
62  */
63 public class IkeAuthDigitalSignPayload extends IkeAuthPayload {
64     private static final String TAG = IkeAuthDigitalSignPayload.class.getSimpleName();
65 
66     private static final String KEY_ALGO_NAME = "RSA";
67     private static final byte SIGNATURE_ALGO_ASN1_BYTES_LEN = (byte) 15;
68     private static final byte SIGNATURE_ALGO_ASN1_BYTES_LEN_LEN = (byte) 1;
69 
70     // Byte arrays of DER encoded identifier ASN.1 objects that indicates the algorithm used to
71     // generate the signature, extracted from
72     // <a href="https://tools.ietf.org/html/rfc7427#appendix-A"> RFC 7427. There is no need to
73     // understand the encoding process. They are just constants to indicate the algorithm type.
74     private static final byte[] PKI_ALGO_ID_DER_BYTES_RSA_SHA1 = {
75         (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09,
76         (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
77         (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
78         (byte) 0x05, (byte) 0x05, (byte) 0x00
79     };
80     private static final byte[] PKI_ALGO_ID_DER_BYTES_RSA_SHA2_256 = {
81         (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09,
82         (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
83         (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
84         (byte) 0x0b, (byte) 0x05, (byte) 0x00
85     };
86     private static final byte[] PKI_ALGO_ID_DER_BYTES_RSA_SHA2_384 = {
87         (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09,
88         (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
89         (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
90         (byte) 0x0c, (byte) 0x05, (byte) 0x00
91     };
92     private static final byte[] PKI_ALGO_ID_DER_BYTES_RSA_SHA2_512 = {
93         (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09,
94         (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
95         (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
96         (byte) 0x0d, (byte) 0x05, (byte) 0x00
97     };
98 
99     // Length of ASN.1 object length field.
100     private static final int SIGNATURE_ALGO_ASN1_LEN_LEN = 1;
101 
102     // Currently we only support RSA for signature algorithm.
103     @Retention(RetentionPolicy.SOURCE)
104     @StringDef({
105         SIGNATURE_ALGO_RSA_SHA1,
106         SIGNATURE_ALGO_RSA_SHA2_256,
107         SIGNATURE_ALGO_RSA_SHA2_384,
108         SIGNATURE_ALGO_RSA_SHA2_512
109     })
110     @VisibleForTesting
111     @interface SignatureAlgo {}
112 
113     public static final String SIGNATURE_ALGO_RSA_SHA1 = "SHA1withRSA";
114     public static final String SIGNATURE_ALGO_RSA_SHA2_256 = "SHA256withRSA";
115     public static final String SIGNATURE_ALGO_RSA_SHA2_384 = "SHA384withRSA";
116     public static final String SIGNATURE_ALGO_RSA_SHA2_512 = "SHA512withRSA";
117 
118     // IKEv2 types for hash algorithms.
119     public static final short HASH_ALGORITHM_RSA_SHA1 = 1;
120     public static final short HASH_ALGORITHM_RSA_SHA2_256 = 2;
121     public static final short HASH_ALGORITHM_RSA_SHA2_384 = 3;
122     public static final short HASH_ALGORITHM_RSA_SHA2_512 = 4;
123     public static final short[] ALL_SIGNATURE_ALGO_TYPES =
124             new short[] {
125                 HASH_ALGORITHM_RSA_SHA1,
126                 HASH_ALGORITHM_RSA_SHA2_256,
127                 HASH_ALGORITHM_RSA_SHA2_384,
128                 HASH_ALGORITHM_RSA_SHA2_512
129             };
130     private static final Map<Short, String> SIGNATURE_ALGO_TYPE_TO_NAME = new HashMap<>();
131 
132     static {
SIGNATURE_ALGO_TYPE_TO_NAME.put(HASH_ALGORITHM_RSA_SHA1, SIGNATURE_ALGO_RSA_SHA1)133         SIGNATURE_ALGO_TYPE_TO_NAME.put(HASH_ALGORITHM_RSA_SHA1, SIGNATURE_ALGO_RSA_SHA1);
SIGNATURE_ALGO_TYPE_TO_NAME.put(HASH_ALGORITHM_RSA_SHA2_256, SIGNATURE_ALGO_RSA_SHA2_256)134         SIGNATURE_ALGO_TYPE_TO_NAME.put(HASH_ALGORITHM_RSA_SHA2_256, SIGNATURE_ALGO_RSA_SHA2_256);
SIGNATURE_ALGO_TYPE_TO_NAME.put(HASH_ALGORITHM_RSA_SHA2_384, SIGNATURE_ALGO_RSA_SHA2_384)135         SIGNATURE_ALGO_TYPE_TO_NAME.put(HASH_ALGORITHM_RSA_SHA2_384, SIGNATURE_ALGO_RSA_SHA2_384);
SIGNATURE_ALGO_TYPE_TO_NAME.put(HASH_ALGORITHM_RSA_SHA2_512, SIGNATURE_ALGO_RSA_SHA2_512)136         SIGNATURE_ALGO_TYPE_TO_NAME.put(HASH_ALGORITHM_RSA_SHA2_512, SIGNATURE_ALGO_RSA_SHA2_512);
137     }
138 
139     public final String signatureAndHashAlgos;
140     public final byte[] signature;
141 
IkeAuthDigitalSignPayload( boolean critical, @AuthMethod int authMethod, byte[] authData)142     protected IkeAuthDigitalSignPayload(
143             boolean critical, @AuthMethod int authMethod, byte[] authData)
144             throws IkeProtocolException {
145         super(critical, authMethod);
146         switch (authMethod) {
147             case AUTH_METHOD_RSA_DIGITAL_SIGN:
148                 signatureAndHashAlgos = SIGNATURE_ALGO_RSA_SHA1;
149                 signature = authData;
150                 break;
151             case AUTH_METHOD_GENERIC_DIGITAL_SIGN:
152                 ByteBuffer inputBuffer = ByteBuffer.wrap(authData);
153 
154                 // Get signature algorithm.
155                 int signAlgoLen = Byte.toUnsignedInt(inputBuffer.get());
156                 byte[] signAlgoBytes = new byte[signAlgoLen];
157                 inputBuffer.get(signAlgoBytes);
158                 signatureAndHashAlgos = bytesToJavaStandardSignAlgoName(signAlgoBytes);
159 
160                 // Get signature.
161                 signature = new byte[authData.length - SIGNATURE_ALGO_ASN1_LEN_LEN - signAlgoLen];
162                 inputBuffer.get(signature);
163                 break;
164             default:
165                 throw new IllegalArgumentException("Unrecognized authentication method.");
166         }
167     }
168 
169     /**
170      * Construct IkeAuthDigitalSignPayload for an outbound IKE packet.
171      *
172      * <p>Since IKE library is always a client, outbound IkeAuthDigitalSignPayload always signs IKE
173      * initiator's SignedOctets, which is concatenation of the IKE_INIT request message, the Nonce
174      * of IKE responder and the signed ID-Initiator payload body.
175      *
176      * <p>Caller MUST validate that the signatureAlgoName is supported by IKE library.
177      *
178      * @param genericSignAuthAlgos peer supported signature hash algorithms that can be used with
179      *     AUTH_METHOD_GENERIC_DIGITAL_SIGN, or an empty set if peer does not support
180      *     AUTH_METHOD_GENERIC_DIGITAL_SIGN
181      * @param privateKey the private key of the identity whose signature is going to be generated.
182      * @param ikeInitBytes IKE_INIT request for calculating IKE initiator's SignedOctets.
183      * @param nonce nonce of IKE responder for calculating IKE initiator's SignedOctets.
184      * @param idPayloadBodyBytes ID-Initiator payload body for calculating IKE initiator's
185      *     SignedOctets.
186      * @param ikePrf the negotiated PRF.
187      * @param prfKeyBytes the negotiated PRF initiator key.
188      */
IkeAuthDigitalSignPayload( Set<Short> genericSignAuthAlgos, PrivateKey privateKey, byte[] ikeInitBytes, byte[] nonce, byte[] idPayloadBodyBytes, IkeMacPrf ikePrf, byte[] prfKeyBytes)189     public IkeAuthDigitalSignPayload(
190             Set<Short> genericSignAuthAlgos,
191             PrivateKey privateKey,
192             byte[] ikeInitBytes,
193             byte[] nonce,
194             byte[] idPayloadBodyBytes,
195             IkeMacPrf ikePrf,
196             byte[] prfKeyBytes) {
197         super(false, getAuthMethod(genericSignAuthAlgos));
198 
199         byte[] dataToSignBytes =
200                 getSignedOctets(ikeInitBytes, nonce, idPayloadBodyBytes, ikePrf, prfKeyBytes);
201 
202         String signatureAlgoName = null;
203         switch (authMethod) {
204             case AUTH_METHOD_RSA_DIGITAL_SIGN:
205                 signatureAlgoName = SIGNATURE_ALGO_RSA_SHA1;
206                 break;
207             case AUTH_METHOD_GENERIC_DIGITAL_SIGN:
208                 signatureAlgoName = selectGenericSignAuthAlgo(genericSignAuthAlgos);
209                 break;
210             default:
211                 throw new IllegalStateException("Invalid auth method: " + authMethod);
212         }
213 
214         try {
215             Signature signGen = Signature.getInstance(signatureAlgoName);
216             signGen.initSign(privateKey);
217             signGen.update(dataToSignBytes);
218 
219             signature = signGen.sign();
220             signatureAndHashAlgos = signatureAlgoName;
221         } catch (SignatureException | InvalidKeyException e) {
222             throw new IllegalArgumentException("Signature generation failed", e);
223         } catch (NoSuchAlgorithmException e) {
224             throw new ProviderException(
225                     "Security Provider does not support "
226                             + KEY_ALGO_NAME
227                             + " or "
228                             + signatureAlgoName);
229         }
230     }
231 
getAuthMethod(Set<Short> genericSignAuthAlgos)232     private static int getAuthMethod(Set<Short> genericSignAuthAlgos) {
233         if (genericSignAuthAlgos.isEmpty()) {
234             return AUTH_METHOD_RSA_DIGITAL_SIGN;
235         }
236         return AUTH_METHOD_GENERIC_DIGITAL_SIGN;
237     }
238 
239     @VisibleForTesting
selectGenericSignAuthAlgo(Set<Short> genericSignAuthAlgos)240     static String selectGenericSignAuthAlgo(Set<Short> genericSignAuthAlgos) {
241         List<Short> algoList = new ArrayList<>(genericSignAuthAlgos);
242         Collections.sort(algoList);
243 
244         // NOTE: For all the currently supported algorithms, the larger the algorithm code, the
245         // stronger it is. This conclusion might not be true anymore when new algorithms are added
246         // and at that time this method will need to be updated.
247         short strongestAlgo = algoList.get(algoList.size() - 1);
248         return SIGNATURE_ALGO_TYPE_TO_NAME.get(strongestAlgo);
249     }
250 
javaStandardSignAlgoNameToAsn1Bytes(String javaSignatureAndHashAlgo)251     private byte[] javaStandardSignAlgoNameToAsn1Bytes(String javaSignatureAndHashAlgo) {
252         switch (javaSignatureAndHashAlgo) {
253             case SIGNATURE_ALGO_RSA_SHA1:
254                 return PKI_ALGO_ID_DER_BYTES_RSA_SHA1;
255             case SIGNATURE_ALGO_RSA_SHA2_256:
256                 return PKI_ALGO_ID_DER_BYTES_RSA_SHA2_256;
257             case SIGNATURE_ALGO_RSA_SHA2_384:
258                 return PKI_ALGO_ID_DER_BYTES_RSA_SHA2_384;
259             case SIGNATURE_ALGO_RSA_SHA2_512:
260                 return PKI_ALGO_ID_DER_BYTES_RSA_SHA2_512;
261             default:
262                 throw new IllegalArgumentException("Impossible! We used an unsupported algo");
263         }
264     }
265 
bytesToJavaStandardSignAlgoName(byte[] signAlgoBytes)266     private String bytesToJavaStandardSignAlgoName(byte[] signAlgoBytes)
267             throws AuthenticationFailedException {
268         if (Arrays.equals(PKI_ALGO_ID_DER_BYTES_RSA_SHA1, signAlgoBytes)) {
269             return SIGNATURE_ALGO_RSA_SHA1;
270         } else if (Arrays.equals(PKI_ALGO_ID_DER_BYTES_RSA_SHA2_256, signAlgoBytes)) {
271             return SIGNATURE_ALGO_RSA_SHA2_256;
272         } else if (Arrays.equals(PKI_ALGO_ID_DER_BYTES_RSA_SHA2_384, signAlgoBytes)) {
273             return SIGNATURE_ALGO_RSA_SHA2_384;
274         } else if (Arrays.equals(PKI_ALGO_ID_DER_BYTES_RSA_SHA2_512, signAlgoBytes)) {
275             return SIGNATURE_ALGO_RSA_SHA2_512;
276         } else {
277             throw new AuthenticationFailedException(
278                     "Unrecognized ASN.1 objects for Signature algorithm and Hash");
279         }
280     }
281 
282     /**
283      * Verify received signature in an inbound IKE packet.
284      *
285      * <p>Since IKE library is always a client, inbound IkeAuthDigitalSignPayload always signs IKE
286      * responder's SignedOctets, which is concatenation of the IKE_INIT response message, the Nonce
287      * of IKE initiator and the signed ID-Responder payload body.
288      *
289      * @param certificate received end certificate to verify the signature.
290      * @param ikeInitBytes IKE_INIT response for calculating IKE responder's SignedOctets.
291      * @param nonce nonce of IKE initiator for calculating IKE responder's SignedOctets.
292      * @param idPayloadBodyBytes ID-Responder payload body for calculating IKE responder's
293      *     SignedOctets.
294      * @param ikePrf the negotiated PRF.
295      * @param prfKeyBytes the negotiated PRF responder key.
296      * @throws AuthenticationFailedException if received signature verification failed.
297      */
verifyInboundSignature( X509Certificate certificate, byte[] ikeInitBytes, byte[] nonce, byte[] idPayloadBodyBytes, IkeMacPrf ikePrf, byte[] prfKeyBytes)298     public void verifyInboundSignature(
299             X509Certificate certificate,
300             byte[] ikeInitBytes,
301             byte[] nonce,
302             byte[] idPayloadBodyBytes,
303             IkeMacPrf ikePrf,
304             byte[] prfKeyBytes)
305             throws AuthenticationFailedException {
306         byte[] dataToSignBytes =
307                 getSignedOctets(ikeInitBytes, nonce, idPayloadBodyBytes, ikePrf, prfKeyBytes);
308 
309         try {
310             Signature signValidator = Signature.getInstance(signatureAndHashAlgos);
311             signValidator.initVerify(certificate);
312             signValidator.update(dataToSignBytes);
313 
314             if (!signValidator.verify(signature)) {
315                 throw new AuthenticationFailedException("Signature verification failed.");
316             }
317         } catch (SignatureException | InvalidKeyException e) {
318             throw new AuthenticationFailedException(e);
319         } catch (NoSuchAlgorithmException e) {
320             throw new ProviderException(
321                     "Security Provider does not support " + signatureAndHashAlgos);
322         }
323     }
324 
325     @Override
encodeAuthDataToByteBuffer(ByteBuffer byteBuffer)326     protected void encodeAuthDataToByteBuffer(ByteBuffer byteBuffer) {
327         if (authMethod == AUTH_METHOD_GENERIC_DIGITAL_SIGN) {
328             byteBuffer.put(SIGNATURE_ALGO_ASN1_BYTES_LEN);
329             byteBuffer.put(javaStandardSignAlgoNameToAsn1Bytes(signatureAndHashAlgos));
330         }
331         byteBuffer.put(signature);
332     }
333 
334     @Override
getAuthDataLength()335     protected int getAuthDataLength() {
336         if (authMethod == AUTH_METHOD_GENERIC_DIGITAL_SIGN) {
337             return SIGNATURE_ALGO_ASN1_BYTES_LEN_LEN
338                     + SIGNATURE_ALGO_ASN1_BYTES_LEN
339                     + signature.length;
340         }
341         return signature.length;
342     }
343 
344     @Override
getTypeString()345     public String getTypeString() {
346         switch (authMethod) {
347             case AUTH_METHOD_RSA_DIGITAL_SIGN:
348                 return "Auth(RSA Digital Sign)";
349             case AUTH_METHOD_GENERIC_DIGITAL_SIGN:
350                 return "Auth(Generic Digital Sign)";
351             default:
352                 throw new IllegalArgumentException("Unrecognized authentication method.");
353         }
354     }
355 
356     /**
357      * Gets the Signature Hash Algorithsm from the specified IkeNotifyPayload.
358      *
359      * @param notifyPayload IkeNotifyPayload to read serialized Signature Hash Algorithms from. The
360      *     payload type must be SIGNATURE_HASH_ALGORITHMS.
361      * @return Set<Short> the Signature Hash Algorithms included in the notifyPayload.
362      * @throws InvalidSyntaxException if the included Signature Hash Algorithms are not serialized
363      *     correctly
364      */
365     @NonNull
getSignatureHashAlgorithmsFromIkeNotifyPayload( IkeNotifyPayload notifyPayload)366     public static Set<Short> getSignatureHashAlgorithmsFromIkeNotifyPayload(
367             IkeNotifyPayload notifyPayload) throws InvalidSyntaxException {
368         if (notifyPayload.notifyType != IkeNotifyPayload.NOTIFY_TYPE_SIGNATURE_HASH_ALGORITHMS) {
369             throw new IllegalArgumentException(
370                     "Notify payload type must be SIGNATURE_HASH_ALGORITHMS");
371         }
372 
373         // Hash Algorithm Identifiers are encoded as 16-bit values with no padding (RFC 7427#4)
374         int dataLen = notifyPayload.notifyData.length;
375         if (dataLen % 2 != 0) {
376             throw new InvalidSyntaxException(
377                     "Received notify(SIGNATURE_HASH_ALGORITHMS) with invalid notify data");
378         }
379 
380         Set<Short> hashAlgos = new ArraySet<>();
381         ByteBuffer serializedHashAlgos = ByteBuffer.wrap(notifyPayload.notifyData);
382         while (serializedHashAlgos.hasRemaining()) {
383             short hashAlgo = serializedHashAlgos.getShort();
384             if (!SIGNATURE_ALGO_TYPE_TO_NAME.containsKey(hashAlgo) || !hashAlgos.add(hashAlgo)) {
385                 getIkeLog().w(TAG, "Unexpected or repeated Signature Hash Algorithm: " + hashAlgo);
386             }
387         }
388 
389         return hashAlgos;
390     }
391 }
392