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