AndroidKeyStoreSignatureSpiBase.java revision ccbe88a505848896e59ef8eb4e8405037ba94e88
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.CallSuper; 20import android.annotation.NonNull; 21import android.os.IBinder; 22import android.security.KeyStore; 23import android.security.KeyStoreException; 24import android.security.keymaster.KeymasterArguments; 25import android.security.keymaster.KeymasterDefs; 26import android.security.keymaster.OperationResult; 27 28import com.android.org.conscrypt.util.EmptyArray; 29 30import java.nio.ByteBuffer; 31import java.security.InvalidKeyException; 32import java.security.InvalidParameterException; 33import java.security.PrivateKey; 34import java.security.ProviderException; 35import java.security.PublicKey; 36import java.security.SecureRandom; 37import java.security.SignatureException; 38import java.security.SignatureSpi; 39 40/** 41 * Base class for {@link SignatureSpi} implementations of Android KeyStore backed ciphers. 42 * 43 * @hide 44 */ 45abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi 46 implements KeyStoreCryptoOperation { 47 private final KeyStore mKeyStore; 48 49 // Fields below are populated by SignatureSpi.engineInitSign/engineInitVerify and KeyStore.begin 50 // and should be preserved after SignatureSpi.engineSign/engineVerify finishes. 51 private boolean mSigning; 52 private AndroidKeyStoreKey mKey; 53 54 /** 55 * Token referencing this operation inside keystore service. It is initialized by 56 * {@code engineInitSign}/{@code engineInitVerify} and is invalidated when 57 * {@code engineSign}/{@code engineVerify} succeeds and on some error conditions in between. 58 */ 59 private IBinder mOperationToken; 60 private long mOperationHandle; 61 private KeyStoreCryptoOperationChunkedStreamer mMessageStreamer; 62 63 /** 64 * Encountered exception which could not be immediately thrown because it was encountered inside 65 * a method that does not throw checked exception. This exception will be thrown from 66 * {@code engineSign} or {@code engineVerify}. Once such an exception is encountered, 67 * {@code engineUpdate} starts ignoring input data. 68 */ 69 private Exception mCachedException; 70 71 AndroidKeyStoreSignatureSpiBase() { 72 mKeyStore = KeyStore.getInstance(); 73 } 74 75 @Override 76 protected final void engineInitSign(PrivateKey key) throws InvalidKeyException { 77 engineInitSign(key, null); 78 } 79 80 @Override 81 protected final void engineInitSign(PrivateKey privateKey, SecureRandom random) 82 throws InvalidKeyException { 83 resetAll(); 84 85 boolean success = false; 86 try { 87 if (privateKey == null) { 88 throw new InvalidKeyException("Unsupported key: null"); 89 } 90 AndroidKeyStoreKey keystoreKey; 91 if (privateKey instanceof AndroidKeyStorePrivateKey) { 92 keystoreKey = (AndroidKeyStoreKey) privateKey; 93 } else { 94 throw new InvalidKeyException("Unsupported private key type: " + privateKey); 95 } 96 mSigning = true; 97 initKey(keystoreKey); 98 appRandom = random; 99 ensureKeystoreOperationInitialized(); 100 success = true; 101 } finally { 102 if (!success) { 103 resetAll(); 104 } 105 } 106 } 107 108 @Override 109 protected final void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { 110 resetAll(); 111 112 boolean success = false; 113 try { 114 if (publicKey == null) { 115 throw new InvalidKeyException("Unsupported key: null"); 116 } 117 AndroidKeyStoreKey keystoreKey; 118 if (publicKey instanceof AndroidKeyStorePublicKey) { 119 keystoreKey = (AndroidKeyStorePublicKey) publicKey; 120 } else { 121 throw new InvalidKeyException("Unsupported public key type: " + publicKey); 122 } 123 mSigning = false; 124 initKey(keystoreKey); 125 appRandom = null; 126 ensureKeystoreOperationInitialized(); 127 success = true; 128 } finally { 129 if (!success) { 130 resetAll(); 131 } 132 } 133 } 134 135 /** 136 * Configures this signature instance to use the provided key. 137 * 138 * @throws InvalidKeyException if the {@code key} is not suitable. 139 */ 140 @CallSuper 141 protected void initKey(AndroidKeyStoreKey key) throws InvalidKeyException { 142 mKey = key; 143 } 144 145 /** 146 * Resets this cipher to its pristine pre-init state. This must be equivalent to obtaining a new 147 * cipher instance. 148 * 149 * <p>Subclasses storing additional state should override this method, reset the additional 150 * state, and then chain to superclass. 151 */ 152 @CallSuper 153 protected void resetAll() { 154 IBinder operationToken = mOperationToken; 155 if (operationToken != null) { 156 mOperationToken = null; 157 mKeyStore.abort(operationToken); 158 } 159 mSigning = false; 160 mKey = null; 161 appRandom = null; 162 mOperationToken = null; 163 mOperationHandle = 0; 164 mMessageStreamer = null; 165 mCachedException = null; 166 } 167 168 /** 169 * Resets this cipher while preserving the initialized state. This must be equivalent to 170 * rolling back the cipher's state to just after the most recent {@code engineInit} completed 171 * successfully. 172 * 173 * <p>Subclasses storing additional post-init state should override this method, reset the 174 * additional state, and then chain to superclass. 175 */ 176 @CallSuper 177 protected void resetWhilePreservingInitState() { 178 IBinder operationToken = mOperationToken; 179 if (operationToken != null) { 180 mOperationToken = null; 181 mKeyStore.abort(operationToken); 182 } 183 mOperationHandle = 0; 184 mMessageStreamer = null; 185 mCachedException = null; 186 } 187 188 private void ensureKeystoreOperationInitialized() throws InvalidKeyException { 189 if (mMessageStreamer != null) { 190 return; 191 } 192 if (mCachedException != null) { 193 return; 194 } 195 if (mKey == null) { 196 throw new IllegalStateException("Not initialized"); 197 } 198 199 KeymasterArguments keymasterInputArgs = new KeymasterArguments(); 200 addAlgorithmSpecificParametersToBegin(keymasterInputArgs); 201 byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( 202 appRandom, getAdditionalEntropyAmountForBegin()); 203 204 OperationResult opResult = mKeyStore.begin( 205 mKey.getAlias(), 206 mSigning ? KeymasterDefs.KM_PURPOSE_SIGN : KeymasterDefs.KM_PURPOSE_VERIFY, 207 true, // permit aborting this operation if keystore runs out of resources 208 keymasterInputArgs, 209 additionalEntropy); 210 if (opResult == null) { 211 throw new KeyStoreConnectException(); 212 } 213 214 // Store operation token and handle regardless of the error code returned by KeyStore to 215 // ensure that the operation gets aborted immediately if the code below throws an exception. 216 mOperationToken = opResult.token; 217 mOperationHandle = opResult.operationHandle; 218 219 // If necessary, throw an exception due to KeyStore operation having failed. 220 InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit( 221 mKeyStore, mKey, opResult.resultCode); 222 if (e != null) { 223 throw e; 224 } 225 226 if (mOperationToken == null) { 227 throw new ProviderException("Keystore returned null operation token"); 228 } 229 if (mOperationHandle == 0) { 230 throw new ProviderException("Keystore returned invalid operation handle"); 231 } 232 233 mMessageStreamer = new KeyStoreCryptoOperationChunkedStreamer( 234 new KeyStoreCryptoOperationChunkedStreamer.MainDataStream( 235 mKeyStore, opResult.token)); 236 } 237 238 @Override 239 public final long getOperationHandle() { 240 return mOperationHandle; 241 } 242 243 @Override 244 protected final void engineUpdate(byte[] b, int off, int len) throws SignatureException { 245 if (mCachedException != null) { 246 throw new SignatureException(mCachedException); 247 } 248 249 try { 250 ensureKeystoreOperationInitialized(); 251 } catch (InvalidKeyException e) { 252 throw new SignatureException(e); 253 } 254 255 if (len == 0) { 256 return; 257 } 258 259 byte[] output; 260 try { 261 output = mMessageStreamer.update(b, off, len); 262 } catch (KeyStoreException e) { 263 throw new SignatureException(e); 264 } 265 266 if (output.length != 0) { 267 throw new ProviderException( 268 "Update operation unexpectedly produced output: " + output.length + " bytes"); 269 } 270 } 271 272 @Override 273 protected final void engineUpdate(byte b) throws SignatureException { 274 engineUpdate(new byte[] {b}, 0, 1); 275 } 276 277 @Override 278 protected final void engineUpdate(ByteBuffer input) { 279 byte[] b; 280 int off; 281 int len = input.remaining(); 282 if (input.hasArray()) { 283 b = input.array(); 284 off = input.arrayOffset() + input.position(); 285 input.position(input.limit()); 286 } else { 287 b = new byte[len]; 288 off = 0; 289 input.get(b); 290 } 291 292 try { 293 engineUpdate(b, off, len); 294 } catch (SignatureException e) { 295 mCachedException = e; 296 } 297 } 298 299 @Override 300 protected final int engineSign(byte[] out, int outOffset, int outLen) 301 throws SignatureException { 302 return super.engineSign(out, outOffset, outLen); 303 } 304 305 @Override 306 protected final byte[] engineSign() throws SignatureException { 307 if (mCachedException != null) { 308 throw new SignatureException(mCachedException); 309 } 310 311 byte[] signature; 312 try { 313 ensureKeystoreOperationInitialized(); 314 signature = mMessageStreamer.doFinal(EmptyArray.BYTE, 0, 0); 315 } catch (InvalidKeyException | KeyStoreException e) { 316 throw new SignatureException(e); 317 } 318 319 resetWhilePreservingInitState(); 320 return signature; 321 } 322 323 @Override 324 protected final boolean engineVerify(byte[] signature) throws SignatureException { 325 if (mCachedException != null) { 326 throw new SignatureException(mCachedException); 327 } 328 329 boolean result; 330 try { 331 ensureKeystoreOperationInitialized(); 332 mMessageStreamer.flush(); 333 OperationResult opResult = mKeyStore.finish(mOperationToken, null, signature); 334 if (opResult == null) { 335 throw new KeyStoreConnectException(); 336 } 337 switch (opResult.resultCode) { 338 case KeyStore.NO_ERROR: 339 result = true; 340 break; 341 case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED: 342 result = false; 343 break; 344 default: 345 throw new SignatureException( 346 KeyStore.getKeyStoreException(opResult.resultCode)); 347 } 348 } catch (InvalidKeyException | KeyStoreException e) { 349 throw new SignatureException(e); 350 } 351 352 resetWhilePreservingInitState(); 353 return result; 354 } 355 356 @Override 357 protected final boolean engineVerify(byte[] sigBytes, int offset, int len) 358 throws SignatureException { 359 return engineVerify(ArrayUtils.subarray(sigBytes, offset, len)); 360 } 361 362 @Deprecated 363 @Override 364 protected final Object engineGetParameter(String param) throws InvalidParameterException { 365 throw new InvalidParameterException(); 366 } 367 368 @Deprecated 369 @Override 370 protected final void engineSetParameter(String param, Object value) 371 throws InvalidParameterException { 372 throw new InvalidParameterException(); 373 } 374 375 protected final KeyStore getKeyStore() { 376 return mKeyStore; 377 } 378 379 /** 380 * Returns {@code true} if this signature is initialized for signing, {@code false} if this 381 * signature is initialized for verification. 382 */ 383 protected final boolean isSigning() { 384 return mSigning; 385 } 386 387 // The methods below need to be implemented by subclasses. 388 389 /** 390 * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's 391 * {@code begin} operation. 392 * 393 * <p>For signature verification, this should be {@code 0} because verification should not be 394 * consuming any entropy. For signature generation, this value should match (or exceed) the 395 * amount of Shannon entropy of the produced signature assuming the key and the message are 396 * known. For example, for ECDSA signature this should be the size of {@code R}, whereas for the 397 * RSA signature with PKCS#1 padding this should be {@code 0}. 398 */ 399 protected abstract int getAdditionalEntropyAmountForBegin(); 400 401 /** 402 * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation. 403 * 404 * @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific 405 * parameters. 406 */ 407 protected abstract void addAlgorithmSpecificParametersToBegin( 408 @NonNull KeymasterArguments keymasterArgs); 409} 410