1/* 2 * Copyright (C) 2015 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 17package android.security.keystore; 18 19import android.annotation.NonNull; 20import android.annotation.Nullable; 21import android.security.keymaster.KeymasterArguments; 22import android.security.keymaster.KeymasterDefs; 23 24import java.security.AlgorithmParameters; 25import java.security.InvalidAlgorithmParameterException; 26import java.security.InvalidKeyException; 27import java.security.Key; 28import java.security.NoSuchAlgorithmException; 29import java.security.ProviderException; 30import java.security.spec.AlgorithmParameterSpec; 31import java.security.spec.InvalidParameterSpecException; 32import java.util.Arrays; 33 34import javax.crypto.CipherSpi; 35import javax.crypto.spec.IvParameterSpec; 36 37/** 38 * Base class for Android Keystore unauthenticated AES {@link CipherSpi} implementations. 39 * 40 * @hide 41 */ 42class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSpiBase { 43 44 abstract static class ECB extends AndroidKeyStoreUnauthenticatedAESCipherSpi { 45 protected ECB(int keymasterPadding) { 46 super(KeymasterDefs.KM_MODE_ECB, keymasterPadding, false); 47 } 48 49 public static class NoPadding extends ECB { 50 public NoPadding() { 51 super(KeymasterDefs.KM_PAD_NONE); 52 } 53 } 54 55 public static class PKCS7Padding extends ECB { 56 public PKCS7Padding() { 57 super(KeymasterDefs.KM_PAD_PKCS7); 58 } 59 } 60 } 61 62 abstract static class CBC extends AndroidKeyStoreUnauthenticatedAESCipherSpi { 63 protected CBC(int keymasterPadding) { 64 super(KeymasterDefs.KM_MODE_CBC, keymasterPadding, true); 65 } 66 67 public static class NoPadding extends CBC { 68 public NoPadding() { 69 super(KeymasterDefs.KM_PAD_NONE); 70 } 71 } 72 73 public static class PKCS7Padding extends CBC { 74 public PKCS7Padding() { 75 super(KeymasterDefs.KM_PAD_PKCS7); 76 } 77 } 78 } 79 80 abstract static class CTR extends AndroidKeyStoreUnauthenticatedAESCipherSpi { 81 protected CTR(int keymasterPadding) { 82 super(KeymasterDefs.KM_MODE_CTR, keymasterPadding, true); 83 } 84 85 public static class NoPadding extends CTR { 86 public NoPadding() { 87 super(KeymasterDefs.KM_PAD_NONE); 88 } 89 } 90 } 91 92 private static final int BLOCK_SIZE_BYTES = 16; 93 94 private final int mKeymasterBlockMode; 95 private final int mKeymasterPadding; 96 /** Whether this transformation requires an IV. */ 97 private final boolean mIvRequired; 98 99 private byte[] mIv; 100 101 /** Whether the current {@code #mIv} has been used by the underlying crypto operation. */ 102 private boolean mIvHasBeenUsed; 103 104 AndroidKeyStoreUnauthenticatedAESCipherSpi( 105 int keymasterBlockMode, 106 int keymasterPadding, 107 boolean ivRequired) { 108 mKeymasterBlockMode = keymasterBlockMode; 109 mKeymasterPadding = keymasterPadding; 110 mIvRequired = ivRequired; 111 } 112 113 @Override 114 protected final void resetAll() { 115 mIv = null; 116 mIvHasBeenUsed = false; 117 super.resetAll(); 118 } 119 120 @Override 121 protected final void resetWhilePreservingInitState() { 122 super.resetWhilePreservingInitState(); 123 } 124 125 @Override 126 protected final void initKey(int opmode, Key key) throws InvalidKeyException { 127 if (!(key instanceof AndroidKeyStoreSecretKey)) { 128 throw new InvalidKeyException( 129 "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null")); 130 } 131 if (!KeyProperties.KEY_ALGORITHM_AES.equalsIgnoreCase(key.getAlgorithm())) { 132 throw new InvalidKeyException( 133 "Unsupported key algorithm: " + key.getAlgorithm() + ". Only " + 134 KeyProperties.KEY_ALGORITHM_AES + " supported"); 135 } 136 setKey((AndroidKeyStoreSecretKey) key); 137 } 138 139 @Override 140 protected final void initAlgorithmSpecificParameters() throws InvalidKeyException { 141 if (!mIvRequired) { 142 return; 143 } 144 145 // IV is used 146 if (!isEncrypting()) { 147 throw new InvalidKeyException("IV required when decrypting" 148 + ". Use IvParameterSpec or AlgorithmParameters to provide it."); 149 } 150 } 151 152 @Override 153 protected final void initAlgorithmSpecificParameters(AlgorithmParameterSpec params) 154 throws InvalidAlgorithmParameterException { 155 if (!mIvRequired) { 156 if (params != null) { 157 throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params); 158 } 159 return; 160 } 161 162 // IV is used 163 if (params == null) { 164 if (!isEncrypting()) { 165 // IV must be provided by the caller 166 throw new InvalidAlgorithmParameterException( 167 "IvParameterSpec must be provided when decrypting"); 168 } 169 return; 170 } 171 if (!(params instanceof IvParameterSpec)) { 172 throw new InvalidAlgorithmParameterException("Only IvParameterSpec supported"); 173 } 174 mIv = ((IvParameterSpec) params).getIV(); 175 if (mIv == null) { 176 throw new InvalidAlgorithmParameterException("Null IV in IvParameterSpec"); 177 } 178 } 179 180 @Override 181 protected final void initAlgorithmSpecificParameters(AlgorithmParameters params) 182 throws InvalidAlgorithmParameterException { 183 if (!mIvRequired) { 184 if (params != null) { 185 throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params); 186 } 187 return; 188 } 189 190 // IV is used 191 if (params == null) { 192 if (!isEncrypting()) { 193 // IV must be provided by the caller 194 throw new InvalidAlgorithmParameterException("IV required when decrypting" 195 + ". Use IvParameterSpec or AlgorithmParameters to provide it."); 196 } 197 return; 198 } 199 200 if (!"AES".equalsIgnoreCase(params.getAlgorithm())) { 201 throw new InvalidAlgorithmParameterException( 202 "Unsupported AlgorithmParameters algorithm: " + params.getAlgorithm() 203 + ". Supported: AES"); 204 } 205 206 IvParameterSpec ivSpec; 207 try { 208 ivSpec = params.getParameterSpec(IvParameterSpec.class); 209 } catch (InvalidParameterSpecException e) { 210 if (!isEncrypting()) { 211 // IV must be provided by the caller 212 throw new InvalidAlgorithmParameterException("IV required when decrypting" 213 + ", but not found in parameters: " + params, e); 214 } 215 mIv = null; 216 return; 217 } 218 mIv = ivSpec.getIV(); 219 if (mIv == null) { 220 throw new InvalidAlgorithmParameterException("Null IV in AlgorithmParameters"); 221 } 222 } 223 224 @Override 225 protected final int getAdditionalEntropyAmountForBegin() { 226 if ((mIvRequired) && (mIv == null) && (isEncrypting())) { 227 // IV will need to be generated 228 return BLOCK_SIZE_BYTES; 229 } 230 231 return 0; 232 } 233 234 @Override 235 protected final int getAdditionalEntropyAmountForFinish() { 236 return 0; 237 } 238 239 @Override 240 protected final void addAlgorithmSpecificParametersToBegin( 241 @NonNull KeymasterArguments keymasterArgs) { 242 if ((isEncrypting()) && (mIvRequired) && (mIvHasBeenUsed)) { 243 // IV is being reused for encryption: this violates security best practices. 244 throw new IllegalStateException( 245 "IV has already been used. Reusing IV in encryption mode violates security best" 246 + " practices."); 247 } 248 249 keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES); 250 keymasterArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode); 251 keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding); 252 if ((mIvRequired) && (mIv != null)) { 253 keymasterArgs.addBytes(KeymasterDefs.KM_TAG_NONCE, mIv); 254 } 255 } 256 257 @Override 258 protected final void loadAlgorithmSpecificParametersFromBeginResult( 259 @NonNull KeymasterArguments keymasterArgs) { 260 mIvHasBeenUsed = true; 261 262 // NOTE: Keymaster doesn't always return an IV, even if it's used. 263 byte[] returnedIv = keymasterArgs.getBytes(KeymasterDefs.KM_TAG_NONCE, null); 264 if ((returnedIv != null) && (returnedIv.length == 0)) { 265 returnedIv = null; 266 } 267 268 if (mIvRequired) { 269 if (mIv == null) { 270 mIv = returnedIv; 271 } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) { 272 throw new ProviderException("IV in use differs from provided IV"); 273 } 274 } else { 275 if (returnedIv != null) { 276 throw new ProviderException( 277 "IV in use despite IV not being used by this transformation"); 278 } 279 } 280 } 281 282 @Override 283 protected final int engineGetBlockSize() { 284 return BLOCK_SIZE_BYTES; 285 } 286 287 @Override 288 protected final int engineGetOutputSize(int inputLen) { 289 return inputLen + 3 * BLOCK_SIZE_BYTES; 290 } 291 292 @Override 293 protected final byte[] engineGetIV() { 294 return ArrayUtils.cloneIfNotEmpty(mIv); 295 } 296 297 @Nullable 298 @Override 299 protected final AlgorithmParameters engineGetParameters() { 300 if (!mIvRequired) { 301 return null; 302 } 303 if ((mIv != null) && (mIv.length > 0)) { 304 try { 305 AlgorithmParameters params = AlgorithmParameters.getInstance("AES"); 306 params.init(new IvParameterSpec(mIv)); 307 return params; 308 } catch (NoSuchAlgorithmException e) { 309 throw new ProviderException( 310 "Failed to obtain AES AlgorithmParameters", e); 311 } catch (InvalidParameterSpecException e) { 312 throw new ProviderException( 313 "Failed to initialize AES AlgorithmParameters with an IV", 314 e); 315 } 316 } 317 return null; 318 } 319} 320