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