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 android.annotation.Nullable; 20 import android.net.IpSecAlgorithm; 21 import android.net.ipsec.ike.SaProposal; 22 import android.util.SparseArray; 23 24 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform; 25 26 import java.security.NoSuchAlgorithmException; 27 import java.security.SecureRandom; 28 29 import javax.crypto.Cipher; 30 import javax.crypto.NoSuchPaddingException; 31 32 /** 33 * IkeCipher contains common information of normal and combined mode encryption algorithms. 34 * 35 * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296, Internet Key Exchange 36 * Protocol Version 2 (IKEv2)</a> 37 */ 38 public abstract class IkeCipher extends IkeCrypto { 39 private static final int KEY_LEN_3DES = 24; 40 private static final int KEY_LEN_CHACHA20_POLY1305 = 32; 41 42 private static final int IV_LEN_3DES = 8; 43 private static final int IV_LEN_AES_CBC = 16; 44 private static final int IV_LEN_AES_CTR = 8; 45 private static final int IV_LEN_AES_GCM = 8; 46 private static final int IV_LEN_CHACHA20_POLY1305 = 8; 47 48 private static final int SALT_LEN_AES_GCM = 4; 49 private static final int SALT_LEN_AES_CTR = 4; 50 private static final int SALT_LEN_AES_CHACHA20_POLY1305 = 4; 51 52 private static final int BLOCK_SIZE_CHACHA_POLY = 4; 53 54 protected static final int SALT_LEN_NOT_INCLUDED = 0; 55 protected static final int BLOCK_SIZE_NOT_SPECIFIED = 0; 56 57 // Map IKE algorithm numbers to IPsec algorithm names 58 private static final SparseArray<String> IKE_ALGO_TO_IPSEC_ALGO; 59 60 static { 61 IKE_ALGO_TO_IPSEC_ALGO = new SparseArray<>(); IKE_ALGO_TO_IPSEC_ALGO.put( SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, IpSecAlgorithm.CRYPT_AES_CBC)62 IKE_ALGO_TO_IPSEC_ALGO.put( 63 SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, IpSecAlgorithm.CRYPT_AES_CBC); IKE_ALGO_TO_IPSEC_ALGO.put( SaProposal.ENCRYPTION_ALGORITHM_AES_CTR, IpSecAlgorithm.CRYPT_AES_CTR)64 IKE_ALGO_TO_IPSEC_ALGO.put( 65 SaProposal.ENCRYPTION_ALGORITHM_AES_CTR, IpSecAlgorithm.CRYPT_AES_CTR); IKE_ALGO_TO_IPSEC_ALGO.put( SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, IpSecAlgorithm.AUTH_CRYPT_AES_GCM)66 IKE_ALGO_TO_IPSEC_ALGO.put( 67 SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, IpSecAlgorithm.AUTH_CRYPT_AES_GCM); IKE_ALGO_TO_IPSEC_ALGO.put( SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, IpSecAlgorithm.AUTH_CRYPT_AES_GCM)68 IKE_ALGO_TO_IPSEC_ALGO.put( 69 SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, IpSecAlgorithm.AUTH_CRYPT_AES_GCM); IKE_ALGO_TO_IPSEC_ALGO.put( SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16, IpSecAlgorithm.AUTH_CRYPT_AES_GCM)70 IKE_ALGO_TO_IPSEC_ALGO.put( 71 SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16, IpSecAlgorithm.AUTH_CRYPT_AES_GCM); IKE_ALGO_TO_IPSEC_ALGO.put( SaProposal.ENCRYPTION_ALGORITHM_CHACHA20_POLY1305, IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305)72 IKE_ALGO_TO_IPSEC_ALGO.put( 73 SaProposal.ENCRYPTION_ALGORITHM_CHACHA20_POLY1305, 74 IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305); 75 } 76 77 private final boolean mIsAead; 78 private final int mIvLen; 79 private final int mBlockSize; 80 81 protected final int mSaltLen; 82 protected final Cipher mCipher; 83 IkeCipher( int algorithmId, int keyLength, int ivLength, String algorithmName, boolean isAead, int saltLen, int blockSize)84 protected IkeCipher( 85 int algorithmId, 86 int keyLength, 87 int ivLength, 88 String algorithmName, 89 boolean isAead, 90 int saltLen, 91 int blockSize) { 92 super(algorithmId, keyLength, algorithmName); 93 mIvLen = ivLength; 94 mIsAead = isAead; 95 mSaltLen = saltLen; 96 97 try { 98 mCipher = Cipher.getInstance(getAlgorithmName()); 99 mBlockSize = blockSize == BLOCK_SIZE_NOT_SPECIFIED ? mCipher.getBlockSize() : blockSize; 100 } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { 101 throw new IllegalArgumentException("Failed to construct " + getTypeString(), e); 102 } 103 } 104 105 /** 106 * Contruct an instance of IkeCipher. 107 * 108 * @param encryptionTransform the valid negotiated EncryptionTransform. 109 * @return an instance of IkeCipher. 110 */ create(EncryptionTransform encryptionTransform)111 public static IkeCipher create(EncryptionTransform encryptionTransform) { 112 int algorithmId = encryptionTransform.id; 113 114 // Use specifiedKeyLength for algorithms with variable key length. Since 115 // specifiedKeyLength are encoded in bits, it needs to be converted to bytes. 116 switch (algorithmId) { 117 case SaProposal.ENCRYPTION_ALGORITHM_3DES: 118 return new IkeNormalModeCipher( 119 algorithmId, KEY_LEN_3DES, IV_LEN_3DES, "DESede/CBC/NoPadding"); 120 case SaProposal.ENCRYPTION_ALGORITHM_AES_CBC: 121 return new IkeNormalModeCipher( 122 algorithmId, 123 encryptionTransform.getSpecifiedKeyLength() / 8, 124 IV_LEN_AES_CBC, 125 "AES/CBC/NoPadding"); 126 case SaProposal.ENCRYPTION_ALGORITHM_AES_CTR: 127 return new IkeNormalModeCipher( 128 algorithmId, 129 encryptionTransform.getSpecifiedKeyLength() / 8, 130 IV_LEN_AES_CTR, 131 "AES/CTR/NoPadding", 132 SALT_LEN_AES_CTR); 133 case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8: 134 // Fall through 135 case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12: 136 // Fall through 137 case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16: 138 // Fall through 139 return new IkeCombinedModeCipher( 140 algorithmId, 141 encryptionTransform.getSpecifiedKeyLength() / 8, 142 IV_LEN_AES_GCM, 143 "AES/GCM/NoPadding", 144 SALT_LEN_AES_GCM); 145 case SaProposal.ENCRYPTION_ALGORITHM_CHACHA20_POLY1305: 146 return new IkeCombinedModeCipher( 147 algorithmId, 148 KEY_LEN_CHACHA20_POLY1305, 149 IV_LEN_CHACHA20_POLY1305, 150 "ChaCha20/Poly1305/NoPadding", 151 SALT_LEN_AES_CHACHA20_POLY1305, 152 BLOCK_SIZE_CHACHA_POLY); 153 default: 154 throw new IllegalArgumentException( 155 "Unrecognized Encryption Algorithm ID: " + algorithmId); 156 } 157 } 158 159 /** 160 * Check if this encryption algorithm is a combined-mode/AEAD algorithm. 161 * 162 * @return if this encryption algorithm is a combined-mode/AEAD algorithm. 163 */ isAead()164 public boolean isAead() { 165 return mIsAead; 166 } 167 168 /** 169 * Get the block size (in bytes). 170 * 171 * @return the block size (in bytes). 172 */ getBlockSize()173 public int getBlockSize() { 174 return mBlockSize; 175 } 176 177 /** 178 * Get initialization vector (IV) length. 179 * 180 * @return the IV length. 181 */ getIvLen()182 public int getIvLen() { 183 return mIvLen; 184 } 185 186 /** 187 * Generate initialization vector (IV). 188 * 189 * @return the initialization vector (IV). 190 */ generateIv()191 public byte[] generateIv() { 192 byte[] iv = new byte[getIvLen()]; 193 new SecureRandom().nextBytes(iv); 194 return iv; 195 } 196 validateKeyLenOrThrow(byte[] key)197 protected void validateKeyLenOrThrow(byte[] key) { 198 if (key.length != getKeyLength()) { 199 throw new IllegalArgumentException( 200 "Expected key with length of : " 201 + getKeyLength() 202 + " Received key with length of : " 203 + key.length); 204 } 205 } 206 207 @Override getKeyLength()208 public int getKeyLength() { 209 return super.getKeyLength() + mSaltLen; 210 } 211 212 /** 213 * Returns the IPsec algorithm name defined in {@link IpSecAlgorithm} given the IKE algorithm 214 * ID. 215 * 216 * <p>Returns null if there is no corresponding IPsec algorithm given the IKE algorithm ID. 217 */ 218 @Nullable getIpSecAlgorithmName(int ikeAlgoId)219 public static String getIpSecAlgorithmName(int ikeAlgoId) { 220 return IKE_ALGO_TO_IPSEC_ALGO.get(ikeAlgoId); 221 } 222 buildIpSecAlgorithmWithKeyImpl(byte[] key)223 protected abstract IpSecAlgorithm buildIpSecAlgorithmWithKeyImpl(byte[] key); 224 225 /** 226 * Build IpSecAlgorithm from this IkeCipher. 227 * 228 * <p>Build IpSecAlgorithm that represents the same encryption algorithm with this IkeCipher 229 * instance with provided encryption key. 230 * 231 * @param key the encryption key in byte array. 232 * @return the IpSecAlgorithm. 233 */ buildIpSecAlgorithmWithKey(byte[] key)234 public IpSecAlgorithm buildIpSecAlgorithmWithKey(byte[] key) { 235 validateKeyLenOrThrow(key); 236 if (getIpSecAlgorithmName(getAlgorithmId()) == null) { 237 throw new IllegalStateException( 238 "Unsupported algorithm " + getAlgorithmId() + " in IPsec"); 239 } 240 return buildIpSecAlgorithmWithKeyImpl(key); 241 } 242 243 /** 244 * Returns algorithm type as a String. 245 * 246 * @return the algorithm type as a String. 247 */ 248 @Override getTypeString()249 public String getTypeString() { 250 return "Encryption Algorithm"; 251 } 252 } 253