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.KeyStoreException; 23 import android.security.KeyStoreOperation; 24 import android.security.keymaster.KeymasterDefs; 25 import android.security.keystore.ArrayUtils; 26 import android.security.keystore.KeyProperties; 27 import android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer.Stream; 28 29 import libcore.util.EmptyArray; 30 31 import java.io.ByteArrayOutputStream; 32 import java.io.IOException; 33 import java.security.AlgorithmParameters; 34 import java.security.InvalidAlgorithmParameterException; 35 import java.security.InvalidKeyException; 36 import java.security.Key; 37 import java.security.NoSuchAlgorithmException; 38 import java.security.ProviderException; 39 import java.security.spec.AlgorithmParameterSpec; 40 import java.security.spec.InvalidParameterSpecException; 41 import java.util.Arrays; 42 import java.util.List; 43 44 import javax.crypto.CipherSpi; 45 import javax.crypto.spec.GCMParameterSpec; 46 47 /** 48 * Base class for Android Keystore authenticated AES {@link CipherSpi} implementations. 49 * 50 * @hide 51 */ 52 abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreCipherSpiBase { 53 54 abstract static class GCM extends AndroidKeyStoreAuthenticatedAESCipherSpi { 55 static final int MIN_SUPPORTED_TAG_LENGTH_BITS = 96; 56 private static final int MAX_SUPPORTED_TAG_LENGTH_BITS = 128; 57 private static final int DEFAULT_TAG_LENGTH_BITS = 128; 58 private static final int IV_LENGTH_BYTES = 12; 59 60 private int mTagLengthBits = DEFAULT_TAG_LENGTH_BITS; 61 GCM(int keymasterPadding)62 GCM(int keymasterPadding) { 63 super(KeymasterDefs.KM_MODE_GCM, keymasterPadding); 64 } 65 66 @Override getTransform()67 protected final String getTransform() { 68 return "AES/GCM/NoPadding"; 69 } 70 71 @Override resetAll()72 protected final void resetAll() { 73 mTagLengthBits = DEFAULT_TAG_LENGTH_BITS; 74 super.resetAll(); 75 } 76 77 @Override resetWhilePreservingInitState()78 protected final void resetWhilePreservingInitState() { 79 super.resetWhilePreservingInitState(); 80 } 81 82 @Override initAlgorithmSpecificParameters()83 protected final void initAlgorithmSpecificParameters() throws InvalidKeyException { 84 if (!isEncrypting()) { 85 throw new InvalidKeyException("IV required when decrypting" 86 + ". Use IvParameterSpec or AlgorithmParameters to provide it."); 87 } 88 } 89 90 @Override initAlgorithmSpecificParameters(AlgorithmParameterSpec params)91 protected final void initAlgorithmSpecificParameters(AlgorithmParameterSpec params) 92 throws InvalidAlgorithmParameterException { 93 // IV is used 94 if (params == null) { 95 if (!isEncrypting()) { 96 // IV must be provided by the caller 97 throw new InvalidAlgorithmParameterException( 98 "GCMParameterSpec must be provided when decrypting"); 99 } 100 return; 101 } 102 if (!(params instanceof GCMParameterSpec)) { 103 throw new InvalidAlgorithmParameterException("Only GCMParameterSpec supported"); 104 } 105 GCMParameterSpec spec = (GCMParameterSpec) params; 106 byte[] iv = spec.getIV(); 107 if (iv == null) { 108 throw new InvalidAlgorithmParameterException("Null IV in GCMParameterSpec"); 109 } else if (iv.length != IV_LENGTH_BYTES) { 110 throw new InvalidAlgorithmParameterException("Unsupported IV length: " 111 + iv.length + " bytes. Only " + IV_LENGTH_BYTES 112 + " bytes long IV supported"); 113 } 114 int tagLengthBits = spec.getTLen(); 115 if ((tagLengthBits < MIN_SUPPORTED_TAG_LENGTH_BITS) 116 || (tagLengthBits > MAX_SUPPORTED_TAG_LENGTH_BITS) 117 || ((tagLengthBits % 8) != 0)) { 118 throw new InvalidAlgorithmParameterException( 119 "Unsupported tag length: " + tagLengthBits + " bits" 120 + ". Supported lengths: 96, 104, 112, 120, 128"); 121 } 122 setIv(iv); 123 mTagLengthBits = tagLengthBits; 124 } 125 126 @Override initAlgorithmSpecificParameters(AlgorithmParameters params)127 protected final void initAlgorithmSpecificParameters(AlgorithmParameters params) 128 throws InvalidAlgorithmParameterException { 129 if (params == null) { 130 if (!isEncrypting()) { 131 // IV must be provided by the caller 132 throw new InvalidAlgorithmParameterException("IV required when decrypting" 133 + ". Use GCMParameterSpec or GCM AlgorithmParameters to provide it."); 134 } 135 return; 136 } 137 138 if (!"GCM".equalsIgnoreCase(params.getAlgorithm())) { 139 throw new InvalidAlgorithmParameterException( 140 "Unsupported AlgorithmParameters algorithm: " + params.getAlgorithm() 141 + ". Supported: GCM"); 142 } 143 144 GCMParameterSpec spec; 145 try { 146 spec = params.getParameterSpec(GCMParameterSpec.class); 147 } catch (InvalidParameterSpecException e) { 148 if (!isEncrypting()) { 149 // IV must be provided by the caller 150 throw new InvalidAlgorithmParameterException("IV and tag length required when" 151 + " decrypting, but not found in parameters: " + params, e); 152 } 153 setIv(null); 154 return; 155 } 156 initAlgorithmSpecificParameters(spec); 157 } 158 159 @Nullable 160 @Override engineGetParameters()161 protected final AlgorithmParameters engineGetParameters() { 162 byte[] iv = getIv(); 163 if ((iv != null) && (iv.length > 0)) { 164 try { 165 AlgorithmParameters params = AlgorithmParameters.getInstance("GCM"); 166 params.init(new GCMParameterSpec(mTagLengthBits, iv)); 167 return params; 168 } catch (NoSuchAlgorithmException e) { 169 throw new ProviderException( 170 "Failed to obtain GCM AlgorithmParameters", e); 171 } catch (InvalidParameterSpecException e) { 172 throw new ProviderException( 173 "Failed to initialize GCM AlgorithmParameters", e); 174 } 175 } 176 return null; 177 } 178 179 @NonNull 180 @Override createMainDataStreamer( KeyStoreOperation operation)181 protected KeyStoreCryptoOperationStreamer createMainDataStreamer( 182 KeyStoreOperation operation) { 183 KeyStoreCryptoOperationStreamer streamer = new KeyStoreCryptoOperationChunkedStreamer( 184 new KeyStoreCryptoOperationChunkedStreamer.MainDataStream( 185 operation), 0); 186 if (isEncrypting()) { 187 return streamer; 188 } else { 189 // When decrypting, to avoid leaking unauthenticated plaintext, do not return any 190 // plaintext before ciphertext is authenticated by KeyStore.finish. 191 return new BufferAllOutputUntilDoFinalStreamer(streamer); 192 } 193 } 194 195 @NonNull 196 @Override createAdditionalAuthenticationDataStreamer( KeyStoreOperation operation)197 protected final KeyStoreCryptoOperationStreamer createAdditionalAuthenticationDataStreamer( 198 KeyStoreOperation operation) { 199 return new KeyStoreCryptoOperationChunkedStreamer( 200 new AdditionalAuthenticationDataStream(operation), 0); 201 } 202 203 @Override getAdditionalEntropyAmountForBegin()204 protected final int getAdditionalEntropyAmountForBegin() { 205 if ((getIv() == null) && (isEncrypting())) { 206 // IV will need to be generated 207 return IV_LENGTH_BYTES; 208 } 209 210 return 0; 211 } 212 213 @Override getAdditionalEntropyAmountForFinish()214 protected final int getAdditionalEntropyAmountForFinish() { 215 return 0; 216 } 217 218 @Override addAlgorithmSpecificParametersToBegin( @onNull List<KeyParameter> parameters)219 protected final void addAlgorithmSpecificParametersToBegin( 220 @NonNull List<KeyParameter> parameters) { 221 super.addAlgorithmSpecificParametersToBegin(parameters); 222 parameters.add(KeyStore2ParameterUtils.makeInt( 223 KeymasterDefs.KM_TAG_MAC_LENGTH, 224 mTagLengthBits 225 )); 226 } 227 getTagLengthBits()228 protected final int getTagLengthBits() { 229 return mTagLengthBits; 230 } 231 232 public static final class NoPadding extends GCM { NoPadding()233 public NoPadding() { 234 super(KeymasterDefs.KM_PAD_NONE); 235 } 236 237 @Override engineGetOutputSize(int inputLen)238 protected final int engineGetOutputSize(int inputLen) { 239 int tagLengthBytes = (getTagLengthBits() + 7) / 8; 240 long result; 241 if (isEncrypting()) { 242 result = getConsumedInputSizeBytes() - getProducedOutputSizeBytes() + inputLen 243 + tagLengthBytes; 244 } else { 245 result = getConsumedInputSizeBytes() - getProducedOutputSizeBytes() + inputLen 246 - tagLengthBytes; 247 } 248 if (result < 0) { 249 return 0; 250 } else if (result > Integer.MAX_VALUE) { 251 return Integer.MAX_VALUE; 252 } 253 return (int) result; 254 } 255 } 256 } 257 258 private static final int BLOCK_SIZE_BYTES = 16; 259 260 private final int mKeymasterBlockMode; 261 private final int mKeymasterPadding; 262 263 private byte[] mIv; 264 265 /** Whether the current {@code #mIv} has been used by the underlying crypto operation. */ 266 private boolean mIvHasBeenUsed; 267 AndroidKeyStoreAuthenticatedAESCipherSpi( int keymasterBlockMode, int keymasterPadding)268 AndroidKeyStoreAuthenticatedAESCipherSpi( 269 int keymasterBlockMode, 270 int keymasterPadding) { 271 mKeymasterBlockMode = keymasterBlockMode; 272 mKeymasterPadding = keymasterPadding; 273 } 274 275 @Override resetAll()276 protected void resetAll() { 277 mIv = null; 278 mIvHasBeenUsed = false; 279 super.resetAll(); 280 } 281 282 @Override initKey(int opmode, Key key)283 protected final void initKey(int opmode, Key key) throws InvalidKeyException { 284 if (!(key instanceof AndroidKeyStoreSecretKey)) { 285 throw new InvalidKeyException( 286 "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null")); 287 } 288 if (!KeyProperties.KEY_ALGORITHM_AES.equalsIgnoreCase(key.getAlgorithm())) { 289 throw new InvalidKeyException( 290 "Unsupported key algorithm: " + key.getAlgorithm() + ". Only " + 291 KeyProperties.KEY_ALGORITHM_AES + " supported"); 292 } 293 setKey((AndroidKeyStoreSecretKey) key); 294 } 295 296 @Override addAlgorithmSpecificParametersToBegin( @onNull List<KeyParameter> parameters)297 protected void addAlgorithmSpecificParametersToBegin( 298 @NonNull List<KeyParameter> parameters) { 299 if ((isEncrypting()) && (mIvHasBeenUsed)) { 300 // IV is being reused for encryption: this violates security best practices. 301 throw new IllegalStateException( 302 "IV has already been used. Reusing IV in encryption mode violates security best" 303 + " practices."); 304 } 305 parameters.add(KeyStore2ParameterUtils.makeEnum( 306 KeymasterDefs.KM_TAG_ALGORITHM, 307 KeymasterDefs.KM_ALGORITHM_AES 308 )); 309 parameters.add(KeyStore2ParameterUtils.makeEnum( 310 KeymasterDefs.KM_TAG_BLOCK_MODE, 311 mKeymasterBlockMode 312 )); 313 parameters.add(KeyStore2ParameterUtils.makeEnum( 314 KeymasterDefs.KM_TAG_PADDING, 315 mKeymasterPadding 316 )); 317 318 if (mIv != null) { 319 parameters.add(KeyStore2ParameterUtils.makeBytes(KeymasterDefs.KM_TAG_NONCE, mIv)); 320 } 321 } 322 323 @Override loadAlgorithmSpecificParametersFromBeginResult( KeyParameter[] parameters)324 protected final void loadAlgorithmSpecificParametersFromBeginResult( 325 KeyParameter[] parameters) { 326 mIvHasBeenUsed = true; 327 328 // NOTE: Keymaster doesn't always return an IV, even if it's used. 329 byte[] returnedIv = null; 330 if (parameters != null) { 331 for (KeyParameter p : parameters) { 332 if (p.tag == KeymasterDefs.KM_TAG_NONCE) { 333 returnedIv = p.value.getBlob(); 334 break; 335 } 336 } 337 } 338 339 if (mIv == null) { 340 mIv = returnedIv; 341 } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) { 342 throw new ProviderException("IV in use differs from provided IV"); 343 } 344 } 345 346 @Override engineGetBlockSize()347 protected final int engineGetBlockSize() { 348 return BLOCK_SIZE_BYTES; 349 } 350 351 @Override engineGetIV()352 protected final byte[] engineGetIV() { 353 return ArrayUtils.cloneIfNotEmpty(mIv); 354 } 355 setIv(byte[] iv)356 protected void setIv(byte[] iv) { 357 mIv = iv; 358 } 359 getIv()360 protected byte[] getIv() { 361 return mIv; 362 } 363 364 /** 365 * {@link KeyStoreCryptoOperationStreamer} which buffers all output until {@code doFinal} from 366 * which it returns all output in one go, provided {@code doFinal} succeeds. 367 */ 368 private static class BufferAllOutputUntilDoFinalStreamer 369 implements KeyStoreCryptoOperationStreamer { 370 371 private final KeyStoreCryptoOperationStreamer mDelegate; 372 private ByteArrayOutputStream mBufferedOutput = new ByteArrayOutputStream(); 373 private long mProducedOutputSizeBytes; 374 BufferAllOutputUntilDoFinalStreamer(KeyStoreCryptoOperationStreamer delegate)375 private BufferAllOutputUntilDoFinalStreamer(KeyStoreCryptoOperationStreamer delegate) { 376 mDelegate = delegate; 377 } 378 379 @Override update(byte[] input, int inputOffset, int inputLength)380 public byte[] update(byte[] input, int inputOffset, int inputLength) 381 throws KeyStoreException { 382 byte[] output = mDelegate.update(input, inputOffset, inputLength); 383 if (output != null) { 384 try { 385 mBufferedOutput.write(output); 386 } catch (IOException e) { 387 throw new ProviderException("Failed to buffer output", e); 388 } 389 } 390 return EmptyArray.BYTE; 391 } 392 393 @Override doFinal(byte[] input, int inputOffset, int inputLength, byte[] signature)394 public byte[] doFinal(byte[] input, int inputOffset, int inputLength, 395 byte[] signature) throws KeyStoreException { 396 byte[] output = mDelegate.doFinal(input, inputOffset, inputLength, signature); 397 if (output != null) { 398 try { 399 mBufferedOutput.write(output); 400 } catch (IOException e) { 401 throw new ProviderException("Failed to buffer output", e); 402 } 403 } 404 byte[] result = mBufferedOutput.toByteArray(); 405 mBufferedOutput.reset(); 406 mProducedOutputSizeBytes += result.length; 407 return result; 408 } 409 410 @Override getConsumedInputSizeBytes()411 public long getConsumedInputSizeBytes() { 412 return mDelegate.getConsumedInputSizeBytes(); 413 } 414 415 @Override getProducedOutputSizeBytes()416 public long getProducedOutputSizeBytes() { 417 return mProducedOutputSizeBytes; 418 } 419 } 420 421 /** 422 * Additional Authentication Data (AAD) stream via a KeyStore streaming operation. This stream 423 * sends AAD into the KeyStore. 424 */ 425 private static class AdditionalAuthenticationDataStream implements Stream { 426 427 private final KeyStoreOperation mOperation; 428 AdditionalAuthenticationDataStream(KeyStoreOperation operation)429 private AdditionalAuthenticationDataStream(KeyStoreOperation operation) { 430 mOperation = operation; 431 } 432 433 @Override update(byte[] input)434 public byte[] update(byte[] input) throws KeyStoreException { 435 mOperation.updateAad(input); 436 return null; 437 } 438 439 @Override finish(byte[] input, byte[] signature)440 public byte[] finish(byte[] input, byte[] signature) { 441 return null; 442 } 443 } 444 }