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.crypto; 18 19 import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_CMAC; 20 import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC; 21 22 import android.net.ipsec.ike.SaProposal; 23 24 import com.android.internal.net.crypto.KeyGenerationUtils; 25 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.PrfTransform; 26 27 import java.nio.ByteBuffer; 28 import java.security.GeneralSecurityException; 29 import java.util.Arrays; 30 31 import javax.crypto.Cipher; 32 import javax.crypto.Mac; 33 34 /** 35 * IkeMacPrf represents a negotiated pseudorandom function. 36 * 37 * <p>Pseudorandom function is usually used for IKE SA authentication and generating keying 38 * materials. 39 * 40 * <p>For pseudorandom functions based on integrity algorithms, all operations will be done by a 41 * {@link Mac}. For pseudorandom functions based on encryption algorithms, all operations will be 42 * done by a {@link Cipher}. 43 * 44 * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296, Internet Key Exchange 45 * Protocol Version 2 (IKEv2)</a> 46 */ 47 public class IkeMacPrf extends IkeMac { 48 private static final int PSEUDORANDOM_FUNCTION_AES128_XCBC_KEY_LEN = 16; 49 IkeMacPrf( @aProposal.PseudorandomFunction int algorithmId, int keyLength, String algorithmName, boolean isJceSupported)50 private IkeMacPrf( 51 @SaProposal.PseudorandomFunction int algorithmId, 52 int keyLength, 53 String algorithmName, 54 boolean isJceSupported) { 55 super(algorithmId, keyLength, algorithmName, isJceSupported); 56 } 57 58 /** 59 * Construct an instance of IkeMacPrf. 60 * 61 * @param prfTransform the valid negotiated PrfTransform. 62 * @return an instance of IkeMacPrf. 63 */ create(PrfTransform prfTransform)64 public static IkeMacPrf create(PrfTransform prfTransform) { 65 int algorithmId = prfTransform.id; 66 67 int keyLength = 0; 68 String algorithmName = ""; 69 boolean isJceSupported = true; 70 71 switch (algorithmId) { 72 case SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1: 73 keyLength = 20; 74 algorithmName = "HmacSHA1"; 75 break; 76 case SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC: 77 keyLength = 16; 78 isJceSupported = false; 79 algorithmName = ALGO_NAME_JCE_UNSUPPORTED; 80 break; 81 case SaProposal.PSEUDORANDOM_FUNCTION_AES128_CMAC: 82 keyLength = 16; 83 algorithmName = "AESCMAC"; 84 break; 85 case SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256: 86 keyLength = 32; 87 algorithmName = "HmacSHA256"; 88 break; 89 case SaProposal.PSEUDORANDOM_FUNCTION_SHA2_384: 90 keyLength = 48; 91 algorithmName = "HmacSHA384"; 92 break; 93 case SaProposal.PSEUDORANDOM_FUNCTION_SHA2_512: 94 keyLength = 64; 95 algorithmName = "HmacSHA512"; 96 break; 97 default: 98 throw new IllegalArgumentException("Unrecognized PRF ID: " + algorithmId); 99 } 100 101 return new IkeMacPrf(algorithmId, keyLength, algorithmName, isJceSupported); 102 } 103 104 @Override signBytes(byte[] keyBytes, byte[] dataToSign)105 public byte[] signBytes(byte[] keyBytes, byte[] dataToSign) { 106 if (getAlgorithmId() == PSEUDORANDOM_FUNCTION_AES128_XCBC) { 107 try { 108 keyBytes = modifyAesXCbcKeyIfNeeded(keyBytes); 109 return new AesXCbcImpl().mac(keyBytes, dataToSign, false /*needTruncation*/); 110 } catch (GeneralSecurityException | IllegalStateException e) { 111 throw new IllegalArgumentException("Failed to generate MAC: ", e); 112 } 113 } else if (getAlgorithmId() == PSEUDORANDOM_FUNCTION_AES128_CMAC) { 114 keyBytes = modifyAesCmacKeyIfNeeded(keyBytes); 115 } 116 117 return super.signBytes(keyBytes, dataToSign); 118 } 119 modifyAesXCbcKeyIfNeeded(byte[] keyBytes)120 private byte[] modifyAesXCbcKeyIfNeeded(byte[] keyBytes) throws GeneralSecurityException { 121 // As per RFC 4434: 122 // The key for AES-XCBC-PRF-128 is created as follows: 123 // 124 // 1. If the key is exactly 128 bits long, use it as-is. 125 // 126 // 2. If the key has fewer than 128 bits, lengthen it to exactly 128 bits by padding it on 127 // the right with zero bits. 128 // 129 // 3. If the key is 129 bits or longer, shorten it to exactly 128 bits by performing the 130 // steps in AES-XCBC-PRF-128 (that is, the algorithm described in this document). In that 131 // re-application of this algorithm, the key is 128 zero bits; the message is the too-long 132 // current key. 133 if (keyBytes.length < 16) { 134 keyBytes = Arrays.copyOf(keyBytes, 16); 135 } else if (keyBytes.length > 16) { 136 keyBytes = new AesXCbcImpl().mac(new byte[16], keyBytes, false /*needTruncation*/); 137 } 138 139 return keyBytes; 140 } 141 modifyAesCmacKeyIfNeeded(byte[] keyBytes)142 private byte[] modifyAesCmacKeyIfNeeded(byte[] keyBytes) { 143 // As per RFC 4615: 144 // The key for AES-CMAC-PRF-128 is created as follows: 145 // 146 // 1. If the key, VK, is exactly 128 bits, then we use it as-is. 147 // 148 // 2. If it is longer or shorter than 128 bits, then we derive the key, K, by applying the 149 // AES-CMAC algorithm using the 128-bit all-zero string as the key and VK as the input 150 // message. 151 if (keyBytes.length != 16) { 152 keyBytes = signBytes(new byte[16], keyBytes); 153 } 154 return keyBytes; 155 } 156 157 /** 158 * Generates SKEYSEED based on the nonces and shared DH secret. 159 * 160 * @param nonceInit the IKE initiator nonce. 161 * @param nonceResp the IKE responder nonce. 162 * @param sharedDhKey the DH shared key. 163 * @return the byte array of SKEYSEED. 164 */ generateSKeySeed(byte[] nonceInit, byte[] nonceResp, byte[] sharedDhKey)165 public byte[] generateSKeySeed(byte[] nonceInit, byte[] nonceResp, byte[] sharedDhKey) { 166 ByteBuffer keyBuffer = null; 167 if (getAlgorithmId() == SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC 168 || getAlgorithmId() == SaProposal.PSEUDORANDOM_FUNCTION_AES128_CMAC) { 169 keyBuffer = ByteBuffer.allocate(getKeyLength()); 170 // When generating initial keys, use 8 bytes each from initiator and responder nonces as 171 // per RFC 7296 172 keyBuffer 173 .put(Arrays.copyOfRange(nonceInit, 0, 8)) 174 .put(Arrays.copyOfRange(nonceResp, 0, 8)); 175 } else { 176 keyBuffer = ByteBuffer.allocate(nonceInit.length + nonceResp.length); 177 keyBuffer.put(nonceInit).put(nonceResp); 178 } 179 180 return signBytes(keyBuffer.array(), sharedDhKey); 181 } 182 183 /** 184 * Generates a rekey SKEYSEED based on the nonces and shared DH secret. 185 * 186 * @param skD the secret for deriving new keys 187 * @param nonceInit the IKE initiator nonce. 188 * @param nonceResp the IKE responder nonce. 189 * @param sharedDhKey the DH shared key. 190 * @return the byte array of SKEYSEED. 191 */ generateRekeyedSKeySeed( byte[] skD, byte[] nonceInit, byte[] nonceResp, byte[] sharedDhKey)192 public byte[] generateRekeyedSKeySeed( 193 byte[] skD, byte[] nonceInit, byte[] nonceResp, byte[] sharedDhKey) { 194 ByteBuffer dataToSign = 195 ByteBuffer.allocate(sharedDhKey.length + nonceInit.length + nonceResp.length); 196 dataToSign.put(sharedDhKey).put(nonceInit).put(nonceResp); 197 198 return signBytes(skD, dataToSign.array()); 199 } 200 201 /** 202 * Derives keying materials from IKE/Child SA negotiation. 203 * 204 * <p>prf+(K, S) outputs a pseudorandom stream by using negotiated PRF iteratively. In this way 205 * it can generate long enough keying material containing all the keys for this IKE/Child SA. 206 * 207 * @see <a href="https://tools.ietf.org/html/rfc7296#section-2.13">RFC 7296 Internet Key 208 * Exchange Protocol Version 2 (IKEv2) 2.13. Generating Keying Material </a> 209 * @param keyBytes the key to sign data. SKEYSEED is used for generating KEYMAT for IKE SA. SK_d 210 * is used for generating KEYMAT for Child SA. 211 * @param dataToSign the data to be signed. 212 * @param keyMaterialLen the length of keying materials. 213 * @return the byte array of keying materials 214 */ generateKeyMat(byte[] keyBytes, byte[] dataToSign, int keyMaterialLen)215 public byte[] generateKeyMat(byte[] keyBytes, byte[] dataToSign, int keyMaterialLen) { 216 return KeyGenerationUtils.prfPlus(this, keyBytes, dataToSign, keyMaterialLen); 217 } 218 219 /** 220 * Returns algorithm type as a String. 221 * 222 * @return the algorithm type as a String. 223 */ 224 @Override getTypeString()225 public String getTypeString() { 226 return "Pseudorandom Function"; 227 } 228 } 229