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.INTEGRITY_ALGORITHM_AES_XCBC_96; 20 21 import android.annotation.Nullable; 22 import android.net.IpSecAlgorithm; 23 import android.net.ipsec.ike.SaProposal; 24 import android.util.SparseArray; 25 26 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform; 27 28 import java.security.GeneralSecurityException; 29 import java.util.Arrays; 30 31 import javax.crypto.Cipher; 32 import javax.crypto.Mac; 33 34 /** 35 * IkeMacIntegrity represents a negotiated integrity algorithm. 36 * 37 * <p>For integrity algorithms based on encryption algorithm, all operations will be done by a 38 * {@link Cipher}. Otherwise, all operations will be done by a {@link Mac}. 39 * 40 * <p>@see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296, Internet Key 41 * Exchange Protocol Version 2 (IKEv2)</a> 42 */ 43 public class IkeMacIntegrity extends IkeMac { 44 // Map IKE algorithm numbers to IPsec algorithm names 45 private static final SparseArray<String> IKE_ALGO_TO_IPSEC_ALGO; 46 47 static { 48 IKE_ALGO_TO_IPSEC_ALGO = new SparseArray<>(); IKE_ALGO_TO_IPSEC_ALGO.put( SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96, IpSecAlgorithm.AUTH_HMAC_SHA1)49 IKE_ALGO_TO_IPSEC_ALGO.put( 50 SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96, IpSecAlgorithm.AUTH_HMAC_SHA1); IKE_ALGO_TO_IPSEC_ALGO.put( SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96, IpSecAlgorithm.AUTH_AES_XCBC)51 IKE_ALGO_TO_IPSEC_ALGO.put( 52 SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96, IpSecAlgorithm.AUTH_AES_XCBC); IKE_ALGO_TO_IPSEC_ALGO.put( SaProposal.INTEGRITY_ALGORITHM_AES_CMAC_96, IpSecAlgorithm.AUTH_AES_CMAC)53 IKE_ALGO_TO_IPSEC_ALGO.put( 54 SaProposal.INTEGRITY_ALGORITHM_AES_CMAC_96, IpSecAlgorithm.AUTH_AES_CMAC); IKE_ALGO_TO_IPSEC_ALGO.put( SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, IpSecAlgorithm.AUTH_HMAC_SHA256)55 IKE_ALGO_TO_IPSEC_ALGO.put( 56 SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, IpSecAlgorithm.AUTH_HMAC_SHA256); IKE_ALGO_TO_IPSEC_ALGO.put( SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, IpSecAlgorithm.AUTH_HMAC_SHA384)57 IKE_ALGO_TO_IPSEC_ALGO.put( 58 SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, IpSecAlgorithm.AUTH_HMAC_SHA384); IKE_ALGO_TO_IPSEC_ALGO.put( SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256, IpSecAlgorithm.AUTH_HMAC_SHA512)59 IKE_ALGO_TO_IPSEC_ALGO.put( 60 SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256, IpSecAlgorithm.AUTH_HMAC_SHA512); 61 } 62 63 private final int mChecksumLength; 64 IkeMacIntegrity( @aProposal.IntegrityAlgorithm int algorithmId, int keyLength, String algorithmName, boolean isJceSupported, int checksumLength)65 private IkeMacIntegrity( 66 @SaProposal.IntegrityAlgorithm int algorithmId, 67 int keyLength, 68 String algorithmName, 69 boolean isJceSupported, 70 int checksumLength) { 71 super(algorithmId, keyLength, algorithmName, isJceSupported); 72 mChecksumLength = checksumLength; 73 } 74 75 /** 76 * Construct an instance of IkeMacIntegrity. 77 * 78 * @param integrityTransform the valid negotiated IntegrityTransform. 79 * @return an instance of IkeMacIntegrity. 80 */ create(IntegrityTransform integrityTransform)81 public static IkeMacIntegrity create(IntegrityTransform integrityTransform) { 82 int algorithmId = integrityTransform.id; 83 84 int keyLength = 0; 85 String algorithmName = ""; 86 boolean isJceSupported = true; 87 int checksumLength = 0; 88 89 switch (algorithmId) { 90 case SaProposal.INTEGRITY_ALGORITHM_NONE: 91 throw new IllegalArgumentException("Integrity algorithm is not found."); 92 case SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96: 93 keyLength = 20; 94 algorithmName = "HmacSHA1"; 95 checksumLength = 12; 96 break; 97 case SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96: 98 keyLength = 16; 99 isJceSupported = false; 100 algorithmName = ALGO_NAME_JCE_UNSUPPORTED; 101 checksumLength = 12; 102 break; 103 case SaProposal.INTEGRITY_ALGORITHM_AES_CMAC_96: 104 keyLength = 16; 105 algorithmName = "AESCMAC"; 106 checksumLength = 12; 107 break; 108 case SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128: 109 keyLength = 32; 110 algorithmName = "HmacSHA256"; 111 checksumLength = 16; 112 break; 113 case SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192: 114 keyLength = 48; 115 algorithmName = "HmacSHA384"; 116 checksumLength = 24; 117 break; 118 case SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256: 119 keyLength = 64; 120 algorithmName = "HmacSHA512"; 121 checksumLength = 32; 122 break; 123 default: 124 throw new IllegalArgumentException( 125 "Unrecognized Integrity Algorithm ID: " + algorithmId); 126 } 127 128 return new IkeMacIntegrity( 129 algorithmId, keyLength, algorithmName, isJceSupported, checksumLength); 130 } 131 132 @Override signBytes(byte[] keyBytes, byte[] dataToSign)133 public byte[] signBytes(byte[] keyBytes, byte[] dataToSign) { 134 if (getAlgorithmId() == INTEGRITY_ALGORITHM_AES_XCBC_96) { 135 try { 136 return new AesXCbcImpl().mac(keyBytes, dataToSign, true /*needTruncation*/); 137 } catch (GeneralSecurityException | IllegalStateException e) { 138 throw new IllegalArgumentException("Failed to generate MAC: ", e); 139 } 140 } else { 141 return super.signBytes(keyBytes, dataToSign); 142 } 143 } 144 145 /** 146 * Gets integrity checksum length (in bytes). 147 * 148 * <p>IKE defines a fixed truncation length for each integirty algorithm as its checksum length. 149 * 150 * @return the integrity checksum length (in bytes). 151 */ getChecksumLen()152 public int getChecksumLen() { 153 return mChecksumLength; 154 } 155 156 /** 157 * Signs the bytes to generate an integrity checksum. 158 * 159 * @param keyBytes the negotiated integrity key. 160 * @param dataToAuthenticate the data to authenticate. 161 * @return the integrity checksum. 162 */ generateChecksum(byte[] keyBytes, byte[] dataToAuthenticate)163 public byte[] generateChecksum(byte[] keyBytes, byte[] dataToAuthenticate) { 164 if (getKeyLength() != keyBytes.length) { 165 throw new IllegalArgumentException( 166 "Expected key length: " 167 + getKeyLength() 168 + " Received key length: " 169 + keyBytes.length); 170 } 171 172 byte[] signedBytes = signBytes(keyBytes, dataToAuthenticate); 173 return Arrays.copyOfRange(signedBytes, 0, mChecksumLength); 174 } 175 176 /** 177 * Returns the IPsec algorithm name defined in {@link IpSecAlgorithm} given the IKE algorithm 178 * ID. 179 * 180 * <p>Returns null if there is no corresponding IPsec algorithm given the IKE algorithm ID. 181 */ 182 @Nullable getIpSecAlgorithmName(int ikeAlgoId)183 public static String getIpSecAlgorithmName(int ikeAlgoId) { 184 return IKE_ALGO_TO_IPSEC_ALGO.get(ikeAlgoId); 185 } 186 187 /** 188 * Build IpSecAlgorithm from this IkeMacIntegrity. 189 * 190 * <p>Build IpSecAlgorithm that represents the same integrity algorithm with this 191 * IkeMacIntegrity instance with provided integrity key. 192 * 193 * @param key the integrity key in byte array. 194 * @return the IpSecAlgorithm. 195 */ buildIpSecAlgorithmWithKey(byte[] key)196 public IpSecAlgorithm buildIpSecAlgorithmWithKey(byte[] key) { 197 if (key.length != getKeyLength()) { 198 throw new IllegalArgumentException( 199 "Expected key with length of : " 200 + getKeyLength() 201 + " Received key with length of : " 202 + key.length); 203 } 204 if (getIpSecAlgorithmName(getAlgorithmId()) == null) { 205 throw new IllegalStateException( 206 "Unsupported algorithm " + getAlgorithmId() + " in IPsec"); 207 } 208 return new IpSecAlgorithm( 209 getIpSecAlgorithmName(getAlgorithmId()), key, mChecksumLength * 8); 210 } 211 212 /** 213 * Returns algorithm type as a String. 214 * 215 * @return the algorithm type as a String. 216 */ 217 @Override getTypeString()218 public String getTypeString() { 219 return "Integrity Algorithm."; 220 } 221 } 222