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